简体   繁体   中英

Verilog: ternary operator together with arithmetic right shift causing unexpected behavior

First take a look at three code examples for designing a right shift register which allows the user to choose between arithmetic right shift or logical right shift:

Ex1:

module shiftr (data, shamt, arith, result);

    input [8 - 1:0] data;
    input [3 - 1:0] shamt;
    input arith;
    output [8 - 1:0] result;

    wire [8 - 1:0] arith_shift;

    assign arith_shift = $signed(data) >>> shamt;
    assign result = arith ? arith_shift : (data >> shamt);

endmodule

Ex2:

module shiftr (data, shamt, arith, result);

    input [8 - 1:0] data;
    input [3 - 1:0] shamt;
    input arith;
    output [8 - 1:0] result;

  assign result = arith ? (($signed(data)) >>> shamt) : (data >> shamt); 

endmodule

Ex3:

module shiftr (data, shamt, arith, result);

    input [8 - 1:0] data;
    input [3 - 1:0] shamt;
    input arith;
    output reg [8 - 1:0] result;

    wire [8 - 1:0] arith_shift;

    always @(*) begin
        if (arith) result = $signed(data) >>> shamt;
        else result = data >> shamt;
    end

endmodule

my testbench:

module shiftr_tb;

    reg [7:0] data;
    reg [2:0] shamt;
    reg arith;
    wire [7:0] result;
    
    shiftr dut (data, shamt, arith, result);

    initial begin
        $monitor("%b %d %b", data, shamt, result);
        arith = 1'b1;
        data = 8'b1000_0000;
        shamt = 3'd2;
        #10 $finish;
    end

    `ifdef FSDB_ON
        initial begin 
            $fsdbDumpfile("trans.fsdb");
            $fsdbDumpvars(0);
            $fsdbDumpMDA();
        end 
    `endif

endmodule

I thought three examples above will have the same output for my testbench, but actually the second one has unexpected behavior , which outputs "10000000 2 00100000" instead of "10000000 2 11100000" in the first and third examples. Note the only difference between the first and second example is the use of a intermediate variable arith_shift.

Any one can tell me what's happening here? https://www.edaplayground.com/x/S9ec

PS1: I tested on "Icarus verilog" ,"vcs", and "questasim", all the same, so the weird behavior is not likely caused by simulator

PS2: I further checked the schematic generated by these three examples in questasim, the first and third generate correctly 在此处输入图片说明 but the second generates a wrong one在此处输入图片说明

PS3: In Ex4, even if we do signed conversion first, the result is the same as Ex2, which is wrong. Note that I explicitly declare result and sign_data as signed variables does not make a change (not to mention if I do not declare them as signed variables).

Ex4

module shiftr (data, shamt, arith, result);

    input [8 - 1:0] data;
    input [3 - 1:0] shamt;
    input arith;
    output signed [8 - 1:0] result;

    wire signed [8 - 1:0] sign_data;

    assign sign_data = $signed(data);
    assign result = arith ? (sign_data >>> shamt) : (data >> shamt);

endmodule

The reason you are seeing this behavior is because you are mixing signed and unsigned types within the context of the conditional operator. The LRM says that signed types get converted to unsigned by the following rules.

11.8.1 Rules for expression types

If any operand is unsigned, the result is unsigned, regardless of the operator.

and

11.8.2 Steps for evaluating an expression

Propagate the type and size of the expression (or self-determined subexpression) back down to the context-determined operands of the expression. In general, any context-determined operand of an operator shall be the same type and size as the result of the operator.

In both examples 2 and 4, the signed operand is immediately cast back to unsigned because it is in a context with another unsigned operand. If you made both operands signed, then you would be able to do this in a single statement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM