verilog两个变量取反相乘出现bug

起因

最开始我希望做一个16位小数乘16位小数的运算,考虑到整数部分(16位)为零,于是我直接取低16位作为变量来进行计算(以节约电路面积)。这相当于我把一个小数左移了16bit然后取高16位。

a,b为16bit的小数<<<16得到的值,例如a=0.1<<<16=16’b0001100110011001
我需要做如下的计算:

a x b,(1-a) x b,a x (1-b),(1-a) x (1-b)

对应的rtl如下:

temp_r1_ab <= decimal_a * decimal_b;
temp_r1_1ab <= (~decimal_a) * decimal_b;//1-a,a是正小数部分<<<16得到的整数,因此1是溢出的部分不用管,1-a=-a,即a取反
temp_r1_a1b <= decimal_a * (~decimal_b);
temp_r1_1a1b <= (~decimal_a) * (~decimal_b);

注释中解释了为什么1-a直接用~a来表示。

但是结果很奇怪!a x b的结果是正常的约等于0.1,但是(1-a) x b即~a x b 得到的却是0.8999而非0.1。(a=0.5无论如何取反,得到的结果都靠近0.5,因此结果应该是0.1)
在这里插入图片描述
这里解释一下,选中变量右键可以设置数据的表示形式。
在这里插入图片描述
因为a,b一共16位都是小数,因此选择16(从右往左数16位作为小数点的位置),因此波形显示的是0.5以及0.19999。
在这里插入图片描述

做简单验证试验

由于位数太多难以判断出错的原因,因此重新写了一个简单的例子:
在这里插入图片描述
波形如下:
在这里插入图片描述
对变量e增加位宽后:
在这里插入图片描述
可以看到e变量的高位一直在扩展1。寻思没有得到结果,因为d和e之间并没有什么区别,只是~a经过了一个新的连线然后接到d上,从理论上来说不应该有区别的。其次,两个正数(无符号数,没有特别的处理)4‘b0100=4’d4 ,4’b0111=4’d7相乘,结果应该是4’d28=8’b0001_1100,不应该在高位不停扩展1(这应该是算术左移才能在高位扩展1,否则都是扩展的0)

此外,如下代码也会出现自动扩展1的情况(16bit的decimal_a = 0.5=16’b1000_0000_0000_0000)

temp_r1_1ab <= (~decimal_a) * 32'd1;

在这里插入图片描述

虽然修改了中间给一级赋值,但结果仍然高位扩展了1。而上述中间赋值wire[3:0] temp_a =(~a[3:0]);位数相同,因此结果不会出现问题。

wire[31:0] decimal_b_n = (~decimal_b[15:0]);

在这里插入图片描述
如下修改,结果仍然不对。

temp_r1_1ab <= (~decimal_a[15:0]) * decimal_b

小结

两个取反相乘有问题,特别做了一个实验。中间给一个相同位宽的变量寄存/赋值~a,就能正常得到结果。

在这里插入图片描述

解释出现这个问题的原因,再总结一下

根据以上现象推测,在计算过程中遇到取反时,如果左边变量的位宽比右边的变量位宽大等号右边的变量会先自动扩展到左边变量的位宽(因为取反)然后进行计算。解决办法如下:
1.过一级赋值(assign),把取反的过程独立出来。如下所示:

wire[15:0] decimal_b_n = (~decimal_b[15:0]);

always……
	temp_r1_1ab[31:0] <= decimal_b_n[15:0] * decimal_b[15:0];

取反的过程独立,这样送入dsp的变量不会自动扩展。

2.在取反后把高位补零然后乘法。如下所示:

    temp_r1_1ab[31:0] <= {16'b0,(~decimal_a[15:0])} * decimal_b[15:0];//可以不用列出位宽,此处是为了说明而列出变量位宽

送入dsp的数无法自动扩展。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值