Verilog Operators
Verilog has a rich operator set covering arithmetic, logic, bitwise, reduction, shift, comparison, and structural operations. Each maps directly to real hardware — understanding which operator synthesizes to which gate or adder is the core of RTL design.
1. Operator Quick-Reference
| Category | Operators | Returns | Synthesizes To |
|---|---|---|---|
| Arithmetic | + - * / % ** | Vector | Adder, subtractor, multiplier, divider |
| Relational | < > <= >= | 1-bit | Comparator |
| Equality | == != === !== | 1-bit | Comparator (== / !=); sim-only (=== / !==) |
| Logical | && || ! | 1-bit | AND/OR/NOT on boolean operands |
| Bitwise | & | ^ ~^ ~ | Vector | Gate arrays (AND/OR/XOR/XNOR/NOT) |
| Reduction | &a |a ^a ~&a ~|a | 1-bit | N-input gate tree |
| Shift | << >> <<< >>> | Vector | Barrel shifter (variable); wires (constant) |
| Conditional | ? : | Vector | Multiplexer |
| Concatenation | { } | Vector | Wire joins (zero cost) |
| Replication | {n{ }} | Vector | Wire joins (zero cost) |
2. Arithmetic Operators
wire [7:0] a = 8'd50, b = 8'd20; wire [7:0] add_r = a + b; // 70 (truncated to 8 bits) wire [8:0] add_nc = a + b; // 70 (9-bit: carry won't overflow) wire [7:0] sub_r = a - b; // 30 wire [15:0] mul_r = a * b; // 1000 (need 16 bits for N*M result) wire [7:0] div_r = a / b; // 2 (integer division; avoid in RTL) wire [7:0] mod_r = a % b; // 10 (remainder; avoid in RTL) wire [7:0] pow_r = 2 ** 3; // 8 (power; avoid in RTL)
wire [8:0] sum = {1'b0, a} + {1'b0, b}; — the MSB captures the carry. Division and modulo synthesize to huge divider circuits — avoid them in RTL; use shift operators for powers of 2.3. Relational & Equality Operators
wire [3:0] x = 4'd9, y = 4'd5; // Relational — return 1-bit result wire lt = x < y; // 0 (9 < 5: false) wire gt = x > y; // 1 (9 > 5: true) wire lte = x <= y; // 0 wire gte = x >= y; // 1 // Equality wire eq = x == y; // 0 (logical equal; X input → X result) wire neq = x != y; // 1 wire ceq = x === y; // 0 (case equal; X matches X exactly) wire cne = x !== y; // 1 (case not-equal)
=== only in testbenches to catch X values: if (result === 8'hxx) $error("X detected");. It is not synthesizable. In RTL, always use ==.4. Logical Operators
Logical operators treat their operands as single-bit booleans (0 = false, non-zero = true) and always return 1 bit.
wire [3:0] a = 4'b1010; // non-zero → true wire [3:0] b = 4'b0000; // zero → false wire land = a && b; // 0 (true && false = false) wire lor = a || b; // 1 (true || false = true) wire lnot = !a; // 0 (not true = false) // Common RTL usage: enable conditions wire go = valid && !stall && ready;
4'b1010 & 4'b0101 = 4'b0000 (bitwise AND, all bits differ), but 4'b1010 && 4'b0101 = 1 (both non-zero, so logical AND is true). Using & where you meant && in an enable condition is a classic bug.5. Bitwise Operators
Bitwise operators apply the operation to each corresponding bit pair, returning a vector of the same width.
wire [3:0] a = 4'b1100; wire [3:0] b = 4'b1010; wire [3:0] bw_and = a & b; // 4'b1000 (AND each bit) wire [3:0] bw_or = a | b; // 4'b1110 (OR each bit) wire [3:0] bw_xor = a ^ b; // 4'b0110 (XOR each bit) wire [3:0] bw_xnor = a ~^ b; // 4'b1001 (XNOR: ~(a^b)) wire [3:0] bw_not = ~a; // 4'b0011 (invert all bits)
6. Reduction Operators
Reduction operators take a single vector and collapse it to 1 bit by applying the operation across all bits.
wire [7:0] d = 8'b10110101; wire r_and = &d; // 0 (AND all bits: 1&0&1&1&0&1&0&1 = 0) wire r_or = |d; // 1 (OR: at least one bit is 1) wire r_xor = ^d; // 1 (XOR = parity: count of 1s is odd) wire r_nand = ~&d; // 1 (NAND = ~AND) wire r_nor = ~|d; // 0 (NOR = ~OR) // Practical: all-ones detect wire all_ones = &d; // 1 only if every bit is 1 // Practical: non-zero detect (any bit set) wire nonzero = |d; // 1 if any bit is 1 // Practical: odd parity bit wire parity = ^d; // XOR of all bits
7. Shift Operators
| Operator | Name | Fill Bits | Use Case |
|---|---|---|---|
| << | Logical left shift | 0 fills LSBs | Multiply by 2ⁿ (unsigned) |
| >> | Logical right shift | 0 fills MSBs | Divide by 2ⁿ (unsigned) |
| <<< | Arithmetic left shift | 0 fills LSBs (same as <<) | Signed multiply by 2ⁿ |
| >>> | Arithmetic right shift | Sign-bit fills MSBs | Signed divide by 2ⁿ (preserve sign) |
wire [7:0] a = 8'd20; // 0001_0100 wire [7:0] sll = a << 2; // 0101_0000 = 80 (×4) wire [7:0] slr = a >> 2; // 0000_0101 = 5 (÷4) reg signed [7:0] s = -8; // 1111_1000 wire signed [7:0] sar = s >>> 1; // 1111_1100 = -4 (sign extended) wire signed [7:0] slr2= s >> 1; // 0111_1100 = 124 (zero-filled: wrong for signed) // Variable shift (barrel shifter in hardware) wire [7:0] vshift = a << shift_amt; // shift_amt is a runtime signal
8. Conditional Operator (?:)
The ternary conditional operator synthesizes directly to a multiplexer. It is the only way to write a mux in a continuous assignment.
// 2:1 mux assign out = sel ? a : b; // 4:1 mux (nested) assign out4 = (sel == 2'b00) ? d0 : (sel == 2'b01) ? d1 : (sel == 2'b10) ? d2 : d3; // Tri-state (Z output) assign bus = oe ? data : 8'bz;
9. Concatenation & Replication
wire [3:0] hi = 4'b1010; wire [3:0] lo = 4'b0101; // Concatenation: join signals into wider vector wire [7:0] byte = {hi, lo}; // 8'b10100101 // Sign extension: extend 8-bit signed to 16-bit wire [7:0] s8 = 8'shFF; // -1 wire [15:0] s16 = {{8{s8[7]}}, s8}; // 16'hFFFF (-1 in 16-bit) // Replication: {count{value}} wire [7:0] zeros = {8{1'b0}}; // 8'b00000000 wire [7:0] ones = {8{1'b1}}; // 8'b11111111 // Left-hand side concatenation (split a bus into parts) wire [7:0] in_bus; wire [3:0] msn, lsn; assign {msn, lsn} = in_bus; // split into two nibbles
10. Operator Precedence
Higher precedence evaluates first. When in doubt, use parentheses — they are free in hardware and prevent bugs.
| Precedence | Operators |
|---|---|
| Highest (1) | ! ~ &(unary) |(unary) ^(unary) |
| 2 | ** |
| 3 | * / % |
| 4 | + - |
| 5 | << >> <<< >>> |
| 6 | < > <= >= |
| 7 | == != === !== |
| 8 | & (binary) |
| 9 | ^ ~^ (binary) |
| 10 | | (binary) |
| 11 | && |
| 12 | || |
| Lowest (13) | ? : |
11. RTL Design Examples
Parameterizable Barrel Shifter
module barrel_shift #(parameter W=8) ( input [W-1:0] data, input [2:0] amt, input dir, // 0=left, 1=right output [W-1:0] result ); assign result = dir ? (data >> amt) : (data << amt); endmodule
Parity Generator
module parity_gen #(parameter N=8) ( input [N-1:0] data, output odd_parity, output even_parity ); assign even_parity = ^data; // XOR reduction assign odd_parity = ~^data; // XNOR reduction endmodule
Priority Encoder (3 inputs)
module priority_enc ( input [3:0] req, // request lines output [1:0] grant, // encoded grant output valid // at least one request ); assign valid = |req; // reduction OR assign grant = req[3] ? 2'd3 : req[2] ? 2'd2 : req[1] ? 2'd1 : 2'd0; endmodule