The short answer: Verilog operators fall into a few groups: arithmetic (+ − * / % **), relational and equality (== != === !==), logical (! && ||), bitwise (~ & | ^), reduction (unary & | ^ applied to all bits of one operand), shifts (<< >> <<< >>>), concatenation and replication ({ }), and the conditional operator (? :). The two things that cause most bugs are confusing bitwise with logical operators, and not knowing how wide the result of an expression is.
Most Verilog operators look like C, and that’s exactly the trap: some behave differently because they work on vectors and 4-state values. In this post I go through each group with the examples I wish I’d had, then the width and sign rules that cause real bugs.
- Same inputs, different operators
- All operators by group
- Precedence
- Expression width: where bits get lost
- Signed arithmetic
- Concatenation and replication
- What synthesis does with each operator
Table of Contents
Part 1: Same inputs, different operators

Part 2: All operators by group
| Group | Operators | Notes |
|---|---|---|
| Arithmetic | + − * / % ** | * synthesises to a multiplier; / and % only cheaply for powers of two or constants |
| Relational | < <= > >= | 1-bit result; x if any operand bit is x or z |
| Equality | == != === !== | === and !== compare x and z literally; testbench only |
| Logical | ! && || | Treat any non-zero vector as true; 1-bit result |
| Bitwise | ~ & | ^ ~^ | Bit by bit; result as wide as the widest operand |
| Reduction | & ~& | ~| ^ ~^ | Unary: combine all bits of one operand into 1 bit |
| Shift | << >> <<< >>> | >>> sign-extends only if the operand is signed |
| Concatenation | {a, b} {4{a}} | Join or replicate vectors; every part must be sized |
| Conditional | c ? a : b | Synthesises to a mux |
Part 3: Precedence
From highest to lowest. When in doubt, add parentheses; it costs nothing and makes intent clear.
| Level | Operators |
|---|---|
| Highest | Unary + − ! ~ and reduction & | ^ |
| ** | |
| * / % | |
| Binary + − | |
| << >> <<< >>> | |
| < <= > >= | |
| == != === !== | |
| & then ^ ~^ then | | |
| && then || | |
| Lowest | ?: |
A classic trap: a & b == c means a & (b == c), because == binds tighter than bitwise &.
Part 4: Expression width: where bits get lost
Verilog sizes an expression using the widest operand and the left-hand side of the assignment. That’s why this keeps the carry:
wire [7:0] a, b;
wire [8:0] sum;
assign sum = a + b; // evaluated at 9 bits: carry kept
wire [7:0] sum8;
assign sum8 = a + b; // carry is lost
assign {cout, sum8} = a + b; // carry captured with concatenation
But inside a concatenation or a comparison, operands are sized on their own, so {a + b} is only 8 bits. When the carry matters, make the widths explicit.
Part 5: Signed arithmetic
reg signed [7:0] x = -8'sd4;
reg [7:0] u = 8'd2;
x >>> 1 // -2: arithmetic shift keeps the sign
x + u // unsigned! one unsigned operand makes the whole expression unsigned
x + $signed(u) // signed, as intended
If you do signed DSP work, make every operand signed or cast explicitly, and check the results in simulation with negative numbers.
Part 6: Concatenation and replication
assign word = {byte3, byte2, byte1, byte0}; // join
assign swapped = {data[7:0], data[15:8]}; // byte swap
assign sext = {{8{x[7]}}, x}; // sign-extend 8 to 16 bits
assign zeros = {16{1'b0}};
Every item in a concatenation must have a defined width, so {a, 1} is illegal; write {a, 1'b1}.
Part 7: What synthesis does with each operator
- + and −: adders; the tool picks a ripple or faster architecture to meet timing.
- *: a multiplier, which is large. FPGAs map it to DSP blocks.
- / and %: only cheap for powers of two (they become shifts and bit selects). A general divider is huge and slow; build a sequential divider instead.
- Shifts by a constant are just wiring; shifts by a variable become a barrel shifter.
- ? : becomes a multiplexer; a chain of them becomes a priority mux.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my Verilog series: Verilog data types. Next: Modules, ports and hierarchy.
FAQ
What is the difference between & and && in Verilog?
& is bitwise and returns a vector; && is logical and returns 1 bit, treating each operand as true if it’s non-zero.
What is a reduction operator?
A unary operator that combines all bits of one vector into one bit: &a is the AND of all bits, |a the OR, ^a the XOR (parity).
What is the difference between >> and >>>?
>> always shifts in zeros. >>> shifts in the sign bit if the operand is signed, which keeps negative numbers negative.
Can I use / in synthesisable Verilog?
Only cheaply for powers of two or constants. A general divider synthesises to very large, slow logic.
