为什么内联 Verilog 算术移位会将其变成逻辑移位?
Why does inlining a Verilog arithmetic shift turn it into a logical one?
我已阅读 multiple 如何在 Verilog 中实现算术移位。都建议使用 $signed(…) >>> …
。
这在直接将结果分配给连线或寄存器时有效,但在将其用作更大表达式的一部分时无效。即:
reg[2:0] a = $signed(3'b100) >>> 1 ;
产生 3'b110
reg[2:0] a = 1'b1 ? $signed(3'b100) >>> 1 : 3'b000;
产生 3'b010
修复似乎是将计算包装到 $unsigned
:
reg[2:0] a = $unsigned($signed(3'b100) >>> 1) ;
产生 3'b110
reg[2:0] a = 1'b1 ? $unsigned($signed(3'b100) >>> 1) : 3'b000;
产生 3'b110
为什么内联算术移位会使其表现得像一个逻辑移位,为什么在其周围添加 $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
操作数 A
和 B
相互关联。每个操作数的大小都将调整为最大的操作数,并且都必须签名才能保持签名状态。这发生在 before 在编译期间应用运算符。 S
是自行决定的——C
之外的任何东西都不会影响它的大小或符号。此外,C
对 A
和 B
的上下文没有影响
类似地,当你有表达式时
A >>> S
A
是上下文决定的,S
是自我决定的
现在,如果我们将两个表达式合二为一:
C ? A >>> S : B
由于 A
和 B
在相同的上下文中并且 B
是无符号的,因此 A
被转换为无符号的。一旦将 A>>>S
包装在函数调用中(任何类型的函数调用都相同),函数的每个输入参数都有自己的上下文,独立于它所属的表达式的其余部分。 return 值将在上下文中与表达式的其余部分一起处理。
我已阅读 multiple $signed(…) >>> …
。
这在直接将结果分配给连线或寄存器时有效,但在将其用作更大表达式的一部分时无效。即:
reg[2:0] a = $signed(3'b100) >>> 1 ;
产生3'b110
reg[2:0] a = 1'b1 ? $signed(3'b100) >>> 1 : 3'b000;
产生3'b010
修复似乎是将计算包装到 $unsigned
:
reg[2:0] a = $unsigned($signed(3'b100) >>> 1) ;
产生3'b110
reg[2:0] a = 1'b1 ? $unsigned($signed(3'b100) >>> 1) : 3'b000;
产生3'b110
为什么内联算术移位会使其表现得像一个逻辑移位,为什么在其周围添加 $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
操作数 A
和 B
相互关联。每个操作数的大小都将调整为最大的操作数,并且都必须签名才能保持签名状态。这发生在 before 在编译期间应用运算符。 S
是自行决定的——C
之外的任何东西都不会影响它的大小或符号。此外,C
对 A
和 B
类似地,当你有表达式时
A >>> S
A
是上下文决定的,S
是自我决定的
现在,如果我们将两个表达式合二为一:
C ? A >>> S : B
由于 A
和 B
在相同的上下文中并且 B
是无符号的,因此 A
被转换为无符号的。一旦将 A>>>S
包装在函数调用中(任何类型的函数调用都相同),函数的每个输入参数都有自己的上下文,独立于它所属的表达式的其余部分。 return 值将在上下文中与表达式的其余部分一起处理。