Verilog operators enable mathematical computations, logical comparisons, and bit manipulations essential for describing digital circuit behavior. Understanding how synthesis tools translate these operators into hardware gates, comparators, adders, and shifters is fundamental to writing efficient RTL code.

Data that cannot be processed is quite useless, there'll always be some form of calculation required in digital circuits and computer systems. Let's look at some of the operators in Verilog that would enable synthesis tools realize appropriate hardware elements.

15 min read | Beginner to Intermediate

What You'll Learn

  • Apply arithmetic operators (+, -, *, /, %, **) and understand their hardware synthesis implications
  • Distinguish between logical operators (&&, ||, !) and bitwise operators (&, |, ^, ~)
  • Use equality operators (==, !=, ===, !==) correctly for X/Z handling in verification
  • Implement shift operations (<<, >>, <<<, >>>) for efficient multiplication/division by powers of 2

Verilog Arithmetic Operators

Arithmetic operators perform mathematical computations and synthesize into adders, subtractors, multipliers, and dividers. If the second operand of a division or modulus operator is zero, then the result will be X. If either operand of the power operator is real, then the result will also be real. The result will be 1 if the second operand of a power operator is 0 (a0).

OperatorDescriptionSynthesis Hardware
a + ba plus bAdder (ripple-carry or carry-lookahead)
a - ba minus bSubtractor (two's complement adder)
a * ba multiplied by bMultiplier (combinational or pipelined)
a / ba divided by bDivider (expensive, avoid in RTL if possible)
a % ba modulo b (remainder)Divider with remainder output
a ** ba to the power of bNot synthesizable (simulation only)

Division and Modulus:

Division (/) and modulus (%) operators create large, slow combinational logic. Avoid in synthesizable RTL unless b is a power of 2 (use shifts instead). The power operator (**) is for simulation only and does not synthesize.

An example of how arithmetic operators are used is given below.


module des;
 reg [7:0] data1;
 reg [7:0] data2;

 initial begin
 data1 = 45; // 8-bit value
 data2 = 9; // 8-bit value

 $display ("Add + = %d", data1 + data2); // 45 + 9 = 54
 $display ("Sub - = %d", data1 - data2); // 45 - 9 = 36
 $display ("Mul * = %d", data1 * data2); // 45 * 9 = 405, truncated to 8 bits = 149
 $display ("Div / = %d", data1 / data2); // 45 / 9 = 5 (integer division)
 $display ("Mod %% = %d", data1 % data2); // 45 % 9 = 0 (no remainder)
 $display ("Pow ** = %d", data2 ** 2); // 9^2 = 81 (simulation only)
 end
endmodule
Output
ncsim> run
Add + = 54
Sub - = 36
Mul * = 149
Div / = 5
Mod % = 0
Pow ** = 81
ncsim: *W,RNQUIE: Simulation is complete.

Result Width: Multiplication of 8-bit x 8-bit produces up to 16-bit result (45 x 9 = 405 = 0x195). When stored in 8-bit register, upper bits are truncated, giving 0x95 = 149.

Real-World Application

DSP Arithmetic Units: Companies like Texas Instruments and Analog Devices design DSP processors with dedicated MAC (Multiply-Accumulate) units that execute result = result + (a * b) in a single cycle. Modern FPGAs from Xilinx and Intel have hard DSP blocks containing 18x25 or 27x27 multipliers with 48-bit accumulators, optimized for the arithmetic operators used in FIR filters, FFTs, and matrix operations.

Verilog Relational Operators

An expression with the relational operator will result in a 1 if the expression is evaluated to be true, and 0 if it is false. If either of the operands is X or Z, then the result will be X. Relational operators have a lower precedence than arithmetic operators and all relational operators have the same precedence.

OperatorDescriptionSynthesis Hardware
a < ba less than bMagnitude comparator
a > ba greater than bMagnitude comparator
a <= ba less than or equal to bMagnitude comparator with equality check
a >= ba greater than or equal to bMagnitude comparator with equality check

Relational operators synthesize into magnitude comparators. For multi-bit comparisons, the hardware uses ripple comparison or tree comparison logic.


module des;
 reg [7:0] data1;
 reg [7:0] data2;

 initial begin
 data1 = 45;
 data2 = 9;
 $display ("Result for data1 >= data2 : %0d", data1 >= data2); // 45 >= 9 -> 1 (true)

 data1 = 45;
 data2 = 45;
 $display ("Result for data1 <= data2 : %0d", data1 <= data2); // 45 <= 45 -> 1 (true)

 data1 = 9;
 data2 = 8;
 $display ("Result for data1 > data2 : %0d", data1 > data2); // 9 > 8 -> 1 (true)

 data1 = 22;
 data2 = 22;
 $display ("Result for data1 < data2 : %0d", data1 < data2); // 22 < 22 -> 0 (false)
 end
endmodule
Output
ncsim> run
Result for data1 >= data2 : 1
Result for data1 <= data2 : 1
Result for data1 > data2 : 1
Result for data1 < data2 : 0
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Equality Operators

Equality operators have the same precedence amongst them and are lower in precedence than relational operators. The result is 1 if true, and 0 if false. If either of the operands of logical-equality (==) or logical-inequality (!=) is X or Z, then the result will be X. You may use case-equality operator (===) or case-inequality operator (!==) to match including X and Z and will always have a known value.

OperatorDescriptionUse Case
a === ba equal to b, including x and zTestbench verification (checks X/Z)
a !== ba not equal to b, including x and zTestbench verification (checks X/Z)
a == ba equal to b, result can be unknownSynthesizable RTL (X/Z propagates)
a != ba not equal to b, result can be unknownSynthesizable RTL (X/Z propagates)

Case Equality (===) Not Synthesizable:

The === and !== operators compare including X and Z values, which have no hardware equivalent. Use only in testbenches for verification. In RTL, use == and !=.

module des;
 reg [7:0] data1;
 reg [7:0] data2;

 initial begin
 // Case equality (===): X/Z treated as literal values
 data1 = 45; data2 = 9;
 $display ("Result for data1(%0d) === data2(%0d) : %0d", data1, data2, data1 === data2); // 0 (not equal)

 data1 = 'b101x; data2 = 'b1011;
 $display ("Result for data1(%0b) === data2(%0b) : %0d", data1, data2, data1 === data2); // 0 (x != 1)

 data1 = 'b101x; data2 = 'b101x;
 $display ("Result for data1(%0b) === data2(%0b) : %0d", data1, data2, data1 === data2); // 1 (exact match, x == x)

 data1 = 'b101z; data2 = 'b1z00;
 $display ("Result for data1(%0b) !== data2(%0b) : %0d", data1, data2, data1 !== data2); // 1 (not equal)

 // Logical equality (==) and inequality (!=) with known values
 data1 = 39; data2 = 39;
 $display ("Result for data1(%0d) == data2(%0d) : %0d", data1, data2, data1 == data2); // 1 (equal)

 data1 = 14; data2 = 14;
 $display ("Result for data1(%0d) != data2(%0d) : %0d", data1, data2, data1 != data2); // 0 (equal, so != is false)
 end
endmodule
Output
ncsim> run
Result for data1(45) === data2(9) : 0
Result for data1(101x) === data2(1011) : 0
Result for data1(101x) === data2(101x) : 1
Result for data1(101z) !== data2(1z00) : 1
Result for data1(39) == data2(39) : 1
Result for data1(14) != data2(14) : 0
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Logical Operators

The result of a logical and (&&) is 1 or true when both its operands are true or non-zero. The result of a logical or (||) is 1 or true when either of its operands are true or non-zero. If an operand is X, the result is X, unless the other operand already decides it (zero for &&, a non-zero value for ||). The logical negation (!) operator will convert a non-zero or true operand into 0 and a zero or false operand into 1, while an X will remain as an X.

OperatorDescriptionReturns
a && bLogical AND1 if both a AND b are non-zero, else 0
a || bLogical OR1 if a OR b is non-zero, else 0
!aLogical NOT (negation)1 if a is 0, else 0 (converts to boolean)

Logical vs Bitwise: Logical operators (&&, ||, !) return single-bit 0 or 1 based on entire operand being zero/non-zero. Bitwise operators (&, |, ~) operate on each bit individually and return multi-bit results.


module des;
 reg [7:0] data1;
 reg [7:0] data2;

 initial begin
 // Logical AND (&&): returns 1 only if BOTH operands are non-zero
 data1 = 45; data2 = 9;
 $display ("Result of data1(%0d) && data2(%0d) : %0d", data1, data2, data1 && data2); // 1 (both non-zero)

 data1 = 0; data2 = 4;
 $display ("Result of data1(%0d) && data2(%0d) : %0d", data1, data2, data1 && data2); // 0 (data1 is zero)

 data1 = 'dx; data2 = 3;
 $display ("Result of data1(%0d) && data2(%0d) : %0d", data1, data2, data1 && data2); // x (data1 has X)

 data1 = 'b101z; data2 = 5;
 $display ("Result of data1(%0d) && data2(%0d) : %0d", data1, data2, data1 && data2); // 1 (Z treated as non-zero)

 // Logical OR (||): returns 1 if EITHER operand is non-zero
 data1 = 45; data2 = 9;
 $display ("Result of data1(%0d) || data2(%0d) : %0d", data1, data2, data1 || data2); // 1 (both non-zero)

 data1 = 0; data2 = 4;
 $display ("Result of data1(%0d) || data2(%0d) : %0d", data1, data2, data1 || data2); // 1 (data2 non-zero)

 data1 = 'dx; data2 = 3;
 $display ("Result of data1(%0d) || data2(%0d) : %0d", data1, data2, data1 || data2); // 1 (data2 non-zero, short-circuit)

 data1 = 'b101z; data2 = 5;
 $display ("Result of data1(%0d) || data2(%0d) : %0d", data1, data2, data1 || data2); // 1 (both non-zero)

 // Logical NOT (!): converts non-zero to 0, zero to 1
 data1 = 4;
 $display ("Result of !data1(%0d) : %0d", data1, !data1); // 0 (4 is non-zero)

 data1 = 0;
 $display ("Result of !data1(%0d) : %0d", data1, !data1); // 1 (0 becomes 1)
 end
endmodule
Output
ncsim> run
Result of data1(45) && data2(9) : 1
Result of data1(0) && data2(4) : 0
Result of data1(x) && data2(3) : x
Result of data1(Z) && data2(5) : 1
Result of data1(45) || data2(9) : 1
Result of data1(0) || data2(4) : 1
Result of data1(x) || data2(3) : 1
Result of data1(Z) || data2(5) : 1
Result of !data1(4) : 0
Result of !data1(0) : 1
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Bitwise Operators

This operator will combine a bit in one operand with its corresponding bit in the other operand to calculate a single bit result.

Bitwise AND (&) Truth Table

&01xz
00000
101xx
x0xxx
z0xxx

Bitwise OR (|) Truth Table

|01xz
001xx
11111
xx1xx
zx1xx

Common Bitwise Operators:

  • & - Bitwise AND (masks bits)
  • | - Bitwise OR (sets bits)
  • ^ - Bitwise XOR (toggles bits, parity)
  • ~ - Bitwise NOT (inverts all bits)
  • ~& - NAND, ~| - NOR, ~^ or ^~ - XNOR

module des;
 reg data1 [0:3];
 reg data2 [0:3];
 integer i, j;

 initial begin
 // Initialize test values: 0, 1, x, z
 data1[0] = 0; data2[0] = 0;
 data1[1] = 1; data2[1] = 1;
 data1[2] = 'x; data2[2] = 'x;
 data1[3] = 'z; data2[3] = 'z;

 // Test bitwise AND across all combinations
 for (i = 0; i < 4; i = i + 1) begin
 for (j = 0; j < 4; j = j + 1) begin
 $display ("data1(%0d) & data2(%0d) = %0d", data1[i], data2[j], data1[i] & data2[j]);
 end
 end
 end
endmodule
Output
ncsim> run
data1(0) & data2(0) = 0
data1(0) & data2(1) = 0
data1(0) & data2(x) = 0
data1(0) & data2(z) = 0
data1(1) & data2(0) = 0
data1(1) & data2(1) = 1
data1(1) & data2(x) = x
data1(1) & data2(z) = x
data1(x) & data2(0) = 0
data1(x) & data2(1) = x
data1(x) & data2(x) = x
data1(x) & data2(z) = x
data1(z) & data2(0) = 0
data1(z) & data2(1) = x
data1(z) & data2(x) = x
data1(z) & data2(z) = x
ncsim: *W,RNQUIE: Simulation is complete.

Real-World Application

Register Masking in CPUs: Intel and AMD processors use bitwise AND (&) extensively for register masking operations. For example, extracting the lower byte from a 32-bit register: byte_val = reg_val & 32'hFF. ARM processors use bitwise OR (|) to set specific control bits without affecting others: control_reg = control_reg | ENABLE_FLAG. Bitwise XOR (^) is used in parity generation for error detection in cache and memory systems.

Verilog Shift Operators

There are two kinds of shift operators:

  • Logical shift operators: << and >> (fill with zeros)
  • Arithmetic shift operators: <<< and >>> (>>> fills with the sign bit when the operand is signed, <<< fills with zeros)

Shift for Multiply/Divide: Left shift by N (<< N) multiplies by 2N. Right shift by N (>> N) divides by 2N. This synthesizes into simple wiring, much faster than multipliers/dividers.


module des;
 reg [7:0] data;
 integer i;

 initial begin
 // Left shift: multiply by powers of 2
 data = 8'h1; // Start with 0000_0001
 $display ("Original data = 'd%0d or 'b%0b", data, data);
 for (i = 0; i < 8; i +=1 ) begin
 $display ("data << %0d = 'b%b", i, data << i); // Shift left by i positions
 end

 // Right shift: divide by powers of 2
 data = 8'h80; // Start with 1000_0000 (128 decimal)
 $display ("Original data = 'd%0d or 'b%0b", data, data);
 for (i = 0; i < 8; i +=1 ) begin
 $display ("data >> %0d = 'b%b", i, data >> i); // Shift right by i positions
 end

 // Demonstrate zero-filling on right shift
 data = 8'h1;
 $display ("data >> 1 = 'b%b", data >> 1); // 0000_0001 >> 1 = 0000_0000
 end
endmodule
Output
ncsim> run
Original data = 'd1 or 'b00000001
data << 0 = 'b00000001
data << 1 = 'b00000010
data << 2 = 'b00000100
data << 3 = 'b00001000
data << 4 = 'b00010000
data << 5 = 'b00100000
data << 6 = 'b01000000
data << 7 = 'b10000000
Original data = 'd128 or 'b10000000
data >> 0 = 'b10000000
data >> 1 = 'b01000000
data >> 2 = 'b00100000
data >> 3 = 'b00010000
data >> 4 = 'b00001000
data >> 5 = 'b00000100
data >> 6 = 'b00000010
data >> 7 = 'b00000001

data >> 1 = 'b00000000
ncsim: *W,RNQUIE: Simulation is complete.

Operator Precedence Table

Precedence (Highest to Lowest)OperatorsType
1!, ~Unary (logical NOT, bitwise NOT)
2**Power (right-associative)
3*, /, %Multiply, divide, modulus
4+, -Add, subtract
5<<, >>, <<<, >>>Shifts
6<, <=, >, >=Relational
7==, !=, ===, !==Equality
8&Bitwise AND
9^, ~^Bitwise XOR, XNOR
10|Bitwise OR
11&&Logical AND
12||Logical OR
13?:Conditional (ternary)

Use parentheses to make operator precedence explicit: a + (b * c) is clearer than a + b * c, even though both compute the same result -- the parentheses just make the precedence explicit.

Common Beginner Mistakes

Mistake #1: Confusing logical and bitwise operators


reg [3:0] a = 4'b1100;
reg [3:0] b = 4'b1010;

// WRONG: Using logical && instead of bitwise &
wire [3:0] result = a && b; // Returns 1 (both non-zero), NOT 4'b1000

Why it's problematic: Logical && returns single bit 0 or 1 based on whether operands are zero/non-zero. Bitwise & operates on each bit pair. Using logical instead of bitwise creates 1-bit result when you expect multi-bit masking.

Best Practice: Use bitwise & for bit manipulation:


wire [3:0] result = a & b; // Bitwise AND: 4'b1100 & 4'b1010 = 4'b1000

Mistake #2: Using case equality (===) in synthesizable RTL


always @(*) begin
 if (data === 8'hxx) // SYNTHESIS ERROR: === not synthesizable
 result = 0;
end

Why it's problematic: The === operator checks for X and Z values, which have no hardware equivalent (X/Z are simulation-only concepts). Synthesis tools will error or produce unexpected logic.

Best Practice: Use === only in testbenches, == in RTL:


// Testbench: check for X
if (data === 8'hxx) $display("ERROR: Data has X values");

// RTL: use ==
if (data == 8'h00) result = 1; // Synthesizes to equality comparator

Mistake #3: Incorrect shift for signed division


reg signed [7:0] value = -16;
reg signed [7:0] divided = value >> 2; // WRONG: Logical shift on signed

Why it's problematic: Logical right shift (>>) fills with zeros, incorrect for signed division. -16 = 8'b1111_0000. Logical shift >> 2 = 8'b0011_1100 (+60), not -4 as expected.

Best Practice: Use arithmetic right shift (>>>) for signed division:


reg signed [7:0] value = -16;
reg signed [7:0] divided = value >>> 2; // Arithmetic shift: preserves sign bit
// 8'b1111_0000 >>> 2 = 8'b1111_1100 (-4 in two's complement)

Mistake #4: Not accounting for result width in multiplication


reg [7:0] a = 200;
reg [7:0] b = 150;
reg [7:0] product = a * b; // WRONG: Result truncated!

Why it's problematic: 200 x 150 = 30,000 = 0x7530. An 8-bit register can only hold 0-255, so upper bits are lost. Result becomes 0x30 = 48, completely wrong.

Best Practice: Size result register to hold full product:


reg [7:0] a = 200;
reg [7:0] b = 150;
reg [15:0] product = a * b; // 16-bit result holds 8x8 multiplication
// product = 0x7530 = 30,000 (correct)

Summary

In this tutorial, you learned:

  • Arithmetic operators (+, -, *, /, %, **) synthesize into adders, subtractors, and multipliers; avoid / and % in RTL (use shifts for power-of-2 division)
  • Relational operators (<, >, <=, >=) create magnitude comparators and return single-bit true/false results
  • Logical operators (&&, ||, !) work on entire operands (zero/non-zero) while bitwise (&, |, ^, ~) operate on individual bits
  • Equality operators: use == and != in RTL (synthesizable), === and !== in testbenches only (checks X/Z values)
  • Shift operators: << and >> are logical (fill with 0), <<< and >>> are arithmetic (preserve sign bit for signed division)
  • Operator precedence follows mathematical conventions; use parentheses for clarity in complex expressions
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5