首页 > 解决方案 > Verilog:三元运算符与算术右移一起导致意外行为

问题描述

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

例1:

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

例 2:

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

例 3:

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:在Ex4中,即使我们先做有符号转换,结果还是和Ex2一样,是错误的。请注意,我将 result 和 sign_data 显式声明为带符号变量不会进行更改(更不用说如果我不将它们声明为带符号变量)。

防爆4

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

标签: verilog

解决方案


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

11.8.1 表达式类型的规则

如果任何操作数是无符号的,则结果是无符号的,与运算符无关。

11.8.2 计算表达式的步骤

将表达式(或自确定的子表达式)的类型和大小向下传播到表达式的上下文确定的操作数。一般而言,运算符的任何上下文确定的操作数都应与运算符的结果具有相同的类型和大小。

在示例 2 和示例 4 中,有符号操作数立即被强制转换回无符号,因为它位于另一个无符号操作数的上下文中。如果您对两个操作数都进行了签名,那么您将能够在单个语句中执行此操作。


推荐阅读