Verilog:三元运算符与算术右移一起导致意外行为

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

首先看一下设计右移寄存器的三个代码示例,它允许用户在算术右移或逻辑右移之间进行选择:

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

我的测试平台:

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

我认为上面的三个例子对于我的测试台会有相同的输出,但实际上 第二个有意外的行为,它输出“10000000 2 00100000”而不是“10000000 2” 11100000”在第一个和第三个例子中。请注意,第一个示例和第二个示例之间的唯一区别是使用中间变量 arith_shift.

谁能告诉我这是怎么回事? https://www.edaplayground.com/x/S9ec

PS1:我在“Icarus verilog”、“vcs”和“questasim”上进行了测试,都是一样的,所以奇怪的行为不太可能是由模拟器引起的

PS2:我在questasim中进一步查看了这三个例子生成的原理图, 第一个和第三个正确生成 但是第二个生成了一个错误的

PS3: 在例4中,即使我们先做有符号转换,结果也和例2一样,这是错误的。请注意,我明确声明 result 和 sign_data 作为有符号变量不会发生变化(更不用说如果我没有将它们声明为有符号变量)。

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

您看到此行为的原因是您在条件运算符的上下文中混合了有符号和无符号类型。 LRM 表示有符号类型根据以下规则转换为无符号类型。

11.8.1 Rules for expression types

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

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.

在示例 2 和 4 中,有符号操作数立即转换回无符号操作数,因为它与另一个无符号操作数处于上下文中。如果你让 both 个操作数都签名了,那么你就可以在一条语句中做到这一点。