为什么内联 Verilog 算术移位会将其变成逻辑移位?

Why does inlining a Verilog arithmetic shift turn it into a logical one?

我已阅读 multiple 如何在 Verilog 中实现算术移位。都建议使用 $signed(…) >>> …

这在直接将结果分配给连线或寄存器时有效,但在将其用作更大表达式的一部分时无效。即:

修复似乎是将计算包装到 $unsigned

为什么内联算术移位会使其表现得像一个逻辑移位,为什么在其周围添加 $unsigned 可以解决问题?

这是一个最小的工作示例:

module puzzle();
   reg [2:0] a_u = 3'b100 >>> 1; // 3'b010
   reg [2:0] a_s = $signed(3'b100) >>> 1; // 3'b110
   reg [2:0] a_mux = 1'b1 ? $signed(3'b100) >>> 1 : 3'b000; // 3'b010

   reg [2:0] b_u = $unsigned(3'b100 >>> 1); // 3'b010
   reg [2:0] b_s = $unsigned($signed(3'b100) >>> 1); // 3'b110
   reg [2:0] b_mux = 1'b1 ? $unsigned($signed(3'b100) >>> 1) : 3'b000; // 3'b110

   initial begin
      $display("a_u = 3'b%3b", a_u);
      $display("a_s = 3'b%3b", a_s);
      $display("a_mux = 3'b%3b", a_mux);

      $display("b_u = 3'b%3b", b_u);
      $display("b_s = 3'b%3b", b_s);
      $display("b_mux = 3'b%3b", b_mux);
   end
endmodule

输出:

a_u = 3'b010
a_s = 3'b110
a_mux = 3'b010
b_u = 3'b010
b_s = 3'b110
b_mux = 3'b110

我不知道原因,但似乎将 3'b000 转换为 3'sb000 也解决了您的问题。

reg [2:0] a_mux = 1'b1 ? $signed(3'b100) >>> 1 : 3'sb000;

此外,使用 $signed() 代替 $unsigned() 也可以:

reg [2:0] b_mux = 1'b1 ? $signed($signed(3'b100) >>> 1) : 3'b000;

这里有两个 Verilog 表达式规则:

  • 上下文确定与自我确定的表达式
  • 在上下文中,混合有符号和无符号操作数会导致所有内容都无符号

当你有表达式

C ? A : B

操作数 AB 相互关联。每个操作数的大小都将调整为最大的操作数,并且都必须签名才能保持签名状态。这发生在 before 在编译期间应用运算符。 S 是自行决定的——C 之外的任何东西都不会影响它的大小或符号。此外,CAB

的上下文没有影响

类似地,当你有表达式时

A >>> S

A 是上下文决定的,S 是自我决定的

现在,如果我们将两个表达式合二为一:

C ? A >>> S : B

由于 AB 在相同的上下文中并且 B 是无符号的,因此 A 被转换为无符号的。一旦将 A>>>S 包装在函数调用中(任何类型的函数调用都相同),函数的每个输入参数都有自己的上下文,独立于它所属的表达式的其余部分。 return 值将在上下文中与表达式的其余部分一起处理。