八位右移位乘法器
虚假的右移位
其实移位总是相对的,所以右移还是有左移的成分。
左移位乘法器很好理解,因为在列竖式的时候就能看明白,符合我们的常规思维:

也就是说,每一次乘法之后,只要把对应的部分积左移相应的位数,再相加,就可以得到最终的结果,这个过程像极了小学二年级的乘法课,非常的直观了,这里不做赘述~
那么什么是右移呢?我听到有同学说,把乘数和被乘数换一下就是了,像这样:

运算顺序从右往左,理论上结果是正确的,就是看起来有点别扭,我们把它转过来:

这下应该看明白了吧,无非是最后的结果再加移位一次,从而实现一个“虚假的右移位”
真正的右移位
真正的右移位,其实是针对小数计算而言的,我们计算整数乘法的时候,习惯性的左移,根本原因是我们习惯了整数的小数点在最右侧,只要不超过那个小数点,就可以直接相加得结果。
而小数计算,因为小数点在左边的缘故,在计算机当中,就可以根据小数点位置不变的思想,来将部分积右移,从而实现一个右移乘法器。这种思想所成的乘法器,也成为定点乘法器。
以二进制小数计算“0.1101 x 0.1011
”为例:

由图可以看出,最后一位的结果并不参与移位相加的运算,什么意思呢?
我们再画详细点:

计算时,每次得到的部分积,都会与上一次得到的部分积相加,而每次相加结果的最后一位不参与运算,可直接右移转存,则部分积占用的空间显然比左移运算小多了。这样说,想必应该足够明白了吧?
设计文件(方式一)
知道了上述原理,我们来写一个右移位的乘法器,逻辑很清晰,每次根据乘数位是否为0
进行判断,如果是0
,则部分积=前一步的部分积+0
,如果是1
,则部分积=前一步的部分积+被乘数
,同时每次将部分积的最低位存储至结果的低位中,最后一次的部分积则为结果的高位。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| module mul_8_1(result,mul1,mul2); input wire[7:0] mul1; input wire[7:0] mul2; output reg[15:0] res; reg [7:0] A; integer i; reg [7:0] temp_x; always @ (*) begin A=0; for (i=0;i<8;i=i+1) begin if(mul2[i]) A=A+{1'b0,mul1}; else A=A+9'b000000000; temp_x[i]=A[0]; A=A>>1; end res={A,temp_x}; end endmodule
|
在RTL
级电路综合中却很少使用for
循环语句。主要原因就是for
循环会被综合器展开为所有变量情况的执行语句,每个变量独立占用寄存器资源,每条执行语句并不能有效地复用硬件逻辑资源,造成巨大的资源浪费。也就是说,for
语句循环几次,就是将相同的电路复制几次。循环次数越多,综合电路占用面积越大,综合就越慢。
所以我们稍微改改写法,就可以解决for
循环了。
综合电路
23
个元件,32
个IO
口,202
根线

设计文件(方式二)
所幸八位的乘法器也并不算复杂,可以写一个并行计算的部分积。这样也就解决了for
循环的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| `define size 8 module mul_8_1( input wire [`size - 1:0] mul1,mul2, output wire [2*`size - 1:0] res ); wire [`size : 0] A[0:`size-1]; wire [`size -1 : 0] low_pro;
assign A[0] = mul2[0] ? {1'b0,mul1} : 9'd0; assign A[1] = mul2[1] ? ((A[0]>>1) + {1'b0,mul1}) : ((A[0]>>1) + 9'd0); assign A[2] = mul2[2] ? ((A[1]>>1) + {1'b0,mul1}) : ((A[1]>>1) + 9'd0); assign A[3] = mul2[3] ? ((A[2]>>1) + {1'b0,mul1}) : ((A[2]>>1) + 9'd0); assign A[4] = mul2[4] ? ((A[3]>>1) + {1'b0,mul1}) : ((A[3]>>1) + 9'd0); assign A[5] = mul2[5] ? ((A[4]>>1) + {1'b0,mul1}) : ((A[4]>>1) + 9'd0); assign A[6] = mul2[6] ? ((A[5]>>1) + {1'b0,mul1}) : ((A[5]>>1) + 9'd0); assign A[7] = mul2[7] ? ((A[6]>>1) + {1'b0,mul1}) : ((A[6]>>1) + 9'd0);
assign low_pro[0] = A[0][0]; assign low_pro[1] = A[1][0]; assign low_pro[2] = A[2][0]; assign low_pro[3] = A[3][0]; assign low_pro[4] = A[4][0]; assign low_pro[5] = A[5][0]; assign low_pro[6] = A[6][0]; assign low_pro[7] = A[7][0];
assign res = {(A[7] >> 1),low_pro}; endmodule
|
综合电路
23
个元件,32
个IO
口,224
根线,和使用for循环的方式相比,多了几根线??!
好吧果然是我的能力问题,有见解的朋友们请务必说一下,我想学!

测试文件
由于两个都是乘法器,输入和输出端口都一样,则可以用同样的测试文件进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| module mul_tb( ); reg [7:0] mul1,mul2; wire [15:0] res; reg clk; initial begin mul1 <= 8'd7; mul2 <= 8'd3; clk <= 0; end
always # 10 clk = ~clk; always @ (posedge clk) begin mul2 <= mul2 + 1'b1; end mul_8_1 try(.mul1(mul1),.mul2(mul2),.res(res),); endmodule
|
仿真波形
调成无符号十进制数进行观察,如图所示,7x7=49
,7x8=56
,结果正确。
