~ vs ! in SystemVerilog: The Bug the Compiler Won't Catch
On a single-bit signal, ~ and ! give the same result. On a multi-bit signal, they can give opposite answers — and the compiler won't warn you.
Will this block execute? Yes or no.
logic [1:0] status;
assign status = 1;
if (~status) begin
// Does this execute?
end
Take a moment before answering.
If you said "no" — think again.
The Setup
This is a question that separates engineers who understand what their code synthesizes to from engineers who rely on intuition. On a single-bit signal, ~ and ! give the same result. That makes the distinction easy to miss — until you hit a multi-bit signal and something breaks in a way that's nearly impossible to find in simulation.
The code above will execute the if block. Not because of a quirk or a compiler bug. Because of exactly what ~ does, and how if evaluates its condition.
Two Operators, Two Different Questions
~ — Bitwise NOT
The tilde operator asks: flip every bit in this vector. It operates on each bit independently and returns a result with the same width as the input.
logic [3:0] input_vec = 4'b1010;
logic [3:0] result;
assign result = ~input_vec; // result = 4'b0101
If the operand contains x or z bits, those become x in the output. The result is always multi-bit if the input is multi-bit.
! — Logical NOT
The exclamation operator asks: is this entire expression zero? It evaluates the operand as a Boolean condition and returns a single bit.
logic [3:0] some_ones = 4'b0101;
logic result;
assign result = !some_ones; // result = 1'b0 (non-zero → TRUE → !TRUE = FALSE)
Output is always 1 bit: 1'b1 if the operand is zero, 1'b0 if non-zero, 1'bx if indeterminate.
For a single-bit signal, ~ and ! give the same result. For a multi-bit signal, they can give completely different answers.
Why the Example Executes
status is 2 bits wide. The value 1 is stored as 2'b01.
With ~status:
- Bitwise NOT flips every bit:
~(2'b01)=2'b10 - The
ifstatement receives2'b10 - Any non-zero value in an
ifcondition evaluates to TRUE 2'b10is not zero → the block executes
With !status:
2'b01is non-zero → logically TRUE!TRUE=1'b0(FALSE)- The condition is FALSE → the block does not execute
The intent was almost certainly to check "is status zero?" That requires !. Using ~ gives the opposite answer for this value.