$signal 的算术移位在条件表达式中给出不同的结果并且总是阻塞

Arithmetic shift by $signal give different result in conditional expression and always block

我正在为 MIPS 编写 ALU。

我曾经把它写成一个顺序逻辑(或者一个 always 块?我不知道为什么但是 c 必须是一个 reg 所以我猜它是一个时序逻辑?)

好像是组合逻辑

always @* begin
    case (op)
        4'b0000: c = mux_a + mux_b; 
        4'b0001: c = mux_a - mux_b;
        4'b0010: c = mux_b << mux_a[4:0];
        4'b0011: c = mux_b >> mux_a[4:0];
        4'b0100: c = $signed(mux_b) >>> mux_a[4:0]; 
        4'b0101: c = mux_a & mux_b;
        4'b0110: c = mux_a | mux_b;
        4'b0111: c = mux_a ^ mux_b;
        4'b1000: c = ~(mux_a | mux_b);    
        4'b1001: c = (mux_a < mux_b) ? 32'b1 : 32'b0; // sltu
        4'b1010: c = ($signed(mux_a) < $signed(mux_b)) ? 32'b1 : 32'b0; // slt
        4'b1011: c = mux_a + mux_b - 4;
        default: c = 0;
    endcase
end

而且效果很好,4'b0100: c = $signed(mux_b) >>> mux_a[4:0]可以得到正确的输出。但是由于某些原因,我决定使用组合逻辑条件表达式。

assign c =  (op == 4'b0000) ? (mux_a + mux_b) : 
            (op == 4'b0001) ? (mux_a - mux_b) : 
            (op == 4'b0010) ? (mux_b << mux_a[4:0]) : 
            (op == 4'b0011) ? (mux_b >> mux_a[4:0]) : 
            (op == 4'b0100) ? ($signed(mux_b) >>> mux_a[4:0]) : 
            (op == 4'b0101) ? (mux_a & mux_b) : 
            (op == 4'b0110) ? (mux_a | mux_b) : 
            (op == 4'b0111) ? (mux_a ^ mux_b) : 
            (op == 4'b1000) ? (~(mux_a | mux_b)) : 
            (op == 4'b1001) ? ((mux_a < mux_b) ? 32'b1 : 32'b0) : 
            (op == 4'b1010) ? (($signed(mux_a) < $signed(mux_b)) ? 32'b1 : 32'b0) : 
            (op == 4'b1011) ? (mux_a + mux_b - 4) : 
            0;

除了 cwire 之外几乎相同。

我 运行 这段代码告诉我,4294967280 (FFFFFFF0) >>> 8 = 33554431 (1FFFFFF) 这太荒谬了。

PC=        388, Fetched 00000000000100011001101000000011.
  Decoder done. alu=4294967280.
  ALUOP=0100 ALUMASK=10 Calc Result=  33554431
  rs=         0 rt=4294967280
  aluflag = 0000
  Written(0000) 01ffffff to RegAddr:19

但是如果我改用(op == 4'b0100) ? ({ {31{mux_b[32]}}, mux_b} >> mux_a[4:0]) :,我可以获得正确的结果。

谁能告诉我原因?以下是这些变量的定义方式(使用 33 位作为溢出标志)

input [31:0] a;
input [31:0] b;
input [31:0] imm1;
input [31:0] imm0;
input [3:0] op;
input [1:0] mask;

output [31:0] result;
output [3:0] flags;

wire [31:0] _mux_a;
wire [31:0] _mux_b;

wire [32:0] mux_a;
wire [32:0] mux_b;
wire [32:0] c;

assign _mux_a = mask[1] ? imm1 : a;
assign _mux_b = mask[0] ? imm0 : b;
assign mux_a = {_mux_a[31], _mux_a};
assign mux_b = {_mux_b[31], _mux_b};
assign result = c[31:0];

//zf of uf
assign flags = {c[31], result == 0,  c[32:31] == 2'b01,  c[32:31] == 2'b10};

这是 iverilog 中的错误。关于组合 $signed 和条件运算符 ?: 的一些事情。无论您使用的是连续赋值(assign 语句)还是始终块都没有关系。

mux_b[32]为1时,$signed(mux_b) >>> mux_a[4:0]会给出正确的输出,而(1'b1) ? ($signed(mux_b) >>> mux_a[4:0]) : 33'h0会给出错误的输出。

如果需要,可以提交错误 here and here

解决方法是分离操作。示例:

assign op4 = $signed(mux_b) >>> mux_a[4:0];
assign c = (op == 4'b0000) ...
           (op == 4'b0100) ? op4 :
           (op == 4'b0101) ...

(op == 4'b0100) ? ({ {31{mux_b[32]}}, mux_b} >> mux_a[4:0]) :,正如问题中已经指出的那样。

仅供参考:根据我的经验,条件运算符 ?: 经常合成为显式 2:1 mux,即使 4:1 或其他 mux类型更理想。您的体验可能会有所不同。一般来说,我建议对任何大于 3:1.

的复用使用 case 语句