verilogsystem-verilog

Ternary operation not working with unary operation


I am sharing a simple SystemVerilog code where the ternary operation is misbehaving because of the reduction or ("|b") operation. I know many other work arounds. I just wanted to know what's going wrong here under the hood.

module tb();
  bit signed [0:3] a = 5;
  bit signed [0:3] b = -3;
  bit [0:1] op;
  
  bit signed [0:4] result;
  
  initial begin
    op = 0;
    result = 0;
    result = (op == 0)? a+b : (op == 1)? a-b: (op == 2)? ~a : |b;
    $display("%0p %0p %0p", result, a, b);
    
    op = 1;
    result = 0;
    result = (op == 0)? a+b : (op == 1)? a-b: (op == 2)? ~a : |b;
    $display("%0p %0p %0p", result, a, b);
    
    op = 2;
    result = 0;
    result = (op == 0)? a+b : (op == 1)? a-b: (op == 2)? ~a : |b;
    $display("%0b %0b %0p", result, a, b);
    
    op = 3;
    result = 0;
    result = (op == 0)? a+b : (op == 1)? a-b: (op == 2)? a : |b;
    $display("%0p %0p %0p", result, a, b);
  end

endmodule

I am sharing the code here. I am sure that the unary operation is to blame here. if I put only "b" instead of "|b" it will work as expected.


Solution

  • The problem with your code is both the different bit lengths and mixing signed and unsigned operands in the same expression.

    In almost all binary operations, i op j, the operands i and j are transformed in context with one another. That means if the operands are of different lengths, the smaller one gets 0-extended or signed-extended. Sign extension only happens if all operands in that context are signed. The extension of operands happens before applying the operator.

    With ternary operator i ? j : k, i is self-determined and is not in context with j and k which are with each other. The left-hand-side of an assignment is in context with its right-hand-side.

    In your example, result is a 5-bit signed variable, and the variables a and b are both 4-bit signed variables. That means all operands need to be extended to 5-bits. However, with the operation |b, b is self determined but its result is 1-bit unsigned. That result becomes an unsigned operand to the ternary operator, that means the entire expression context is unsigned. So b gets zero-extended instead of sign-extended.

    Section 11.8.2 Steps for evaluating an expression in the IEEE 1800-2023 SystemVerilog LRM goes over this in detail.

    You could work around this problem if