SystemVerilog中三元运算符与always_comb实现乘法的差异及错误原因问询
问题:SystemVerilog有符号/无符号乘法实现差异分析
模块定义
我设计了一个支持有符号/无符号乘法的SystemVerilog模块,模块代码如下:
module signed_or_unsigned_mul # ( parameter n = 8 ) ( input [ n - 1:0] a, b, input signed_mul, output [2 * n - 1:0] res );
两种实现及问题
三元运算符实现(结果错误)
使用三元运算符实现时,部分测试用例结果错误,例如-8 * -7得到72,代码如下:
assign res = signed_mul ? ($signed(a) * $signed(b)) : (a * b) ;
always_comb+if实现(结果正确)
采用always_comb结合if语句的实现能得到正确结果,代码如下:
logic [2 * n - 1:0] res_stub; assign res = res_stub; always_comb if (signed_mul) res_stub = $signed(a) * $signed(b); else res_stub = a * b;
我不清楚这两种实现的差异所在,也不明白为何结果不同,请求指出原因,是否是中间变量导致的?
原因分析
两种实现的核心差异在于SystemVerilog的类型处理规则不同,和中间变量无关:
1. 三元运算符的类型统一规则
SystemVerilog要求三元运算符? :的两个分支表达式必须转换为同一类型,转换遵循以下逻辑:
- 若两个分支位宽不同,窄位宽的表达式会被扩展到宽位宽。
- 若位宽相同但符号性不同,有符号类型会被强制转换为无符号类型。
在你的代码中:
$signed(a) * $signed(b)的结果是有符号16位值。a * b的结果是无符号16位值。
由于两者位宽相同、符号性不同,编译器会将有符号乘法的结果隐式转换为无符号数。如果有符号乘法的结果是负数,转换后会得到一个远大于预期的无符号数值;即使结果是正数,某些边界场景也可能因类型转换逻辑出现异常(比如你的测试用例中出现的错误)。
2. always_comb赋值的位模式直接复制
在always_comb的实现中,res_stub是无符号16位logic变量,赋值操作会直接将右边表达式的二进制位模式复制到左边变量中,不会进行隐式的类型转换:
- 当执行有符号乘法时,
$signed(a)*$signed(b)的补码结果会直接作为二进制位存储到res_stub中,这和有符号数的预期结果在二进制上是一致的(只是解释方式不同,但res作为输出位宽足够,能正确保留补码信息)。 - 无符号乘法的结果则直接以无符号二进制位存储,符合预期。
这种赋值方式跳过了三元运算符的强制类型转换步骤,因此能得到正确结果。
内容的提问来源于stack exchange,提问作者Aleksandr Grigorev
相关产品推荐
相关产品推荐

