Search code examples
verilogsystem-veriloghdl

Why is computing two's compliment in a single Verilog statement (i.e. ~x + 1'b1) producing the wrong answer?


I realized that doing the 2's compliment in a single verilog statement (as seen for the '' below) is giving the wrong answer. But I don't understand why. Can someone help explain this? Operator precedence doesn't seem to explain the issue.

module test();
  logic [7:0] x;
  logic [7:0] x_1s;
  logic [3:0] x_2_0_2s_compl_type1;
  logic [3:0] x_2_0_2s_compl_type2;

  assign x = '0;

  assign x_2_0_2s_compl_type1 = ~x[2:0] + 1'b1; // gives the wrong answer

  assign x_1s = ~x;
  assign x_2_0_2s_compl_type2 = x_1s[2:0] +1'b1; // gives correct answer

  always @* begin
    $display("x = %b",x);
    $display("x_2_0_2s_compl_type1 = %b",x_2_0_2s_compl_type1);
    $display("x_1s = %b",x_1s);
    $display("x_2_0_2s_compl_type2 = %b",x_2_0_2s_compl_type2);
  end

endmodule

Simulation results:

x = 00000000
x_2_0_2s_compl_type1 = 0000
x_1s = 11111111
x_2_0_2s_compl_type2 = 1000

Solution

  • Section 11.8.2 Steps for evaluating an expression in the 1800-2017 LRM explains this. Basically, the operands in a context-determined expression get extended to match the size of the target assignment before applying any operators. So ~x[2:0] becomes ~(4'b000)

    To do what you want in one expression you can write

    assign x_2_0_2s_compl_type1 = {~x}[2:0] + 1'b1;
    

    or

    assign x_2_0_2s_compl_type1 = 3'(~x) + 1'b1;