原码一位乘法器设计_八位右移位乘法器

6d21044f86b20fc2acc433ad175b751d.png

八位右移位乘法器

  • 虚假的右移位
  • 真正的右移位
  • 设计文件(方式一)
    • 综合电路
  • 设计文件(方式二)
    • 综合电路
  • 测试文件
  • 仿真波形

虚假的右移位

其实移位总是相对的,所以右移还是有左移的成分。

左移位乘法器很好理解,因为在列竖式的时候就能看明白,符合我们的常规思维:

a56cd56e53049073925567cf8a3a05ce.png

也就是说,每一次乘法之后,只要把对应的部分积左移相应的位数,再相加,就可以得到最终的结果,这个过程像极了小学二年级的乘法课,非常的直观了,这里不做赘述~

那么什么是右移呢?我听到有同学说,把乘数和被乘数换一下就是了,像这样:

039e6b4170725b91a8880a7ef236271b.png

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

a93f76b39bfdaf01ffd38863454d0e09.png

这下应该看明白了吧,无非是最后的结果再加移位一次,从而实现一个“虚假的右移位”

真正的右移位

真正的右移位,其实是针对小数计算而言的,我们计算整数乘法的时候,习惯性的左移,根本原因是我们习惯了整数的小数点在最右侧,只要不超过那个小数点,就可以直接相加得结果。

而小数计算,因为小数点在左边的缘故,在计算机当中,就可以根据小数点位置不变的思想,来将部分积右移,从而实现一个右移乘法器。这种思想所成的乘法器,也成为定点乘法器

以二进制小数计算“0.1101 x 0.1011”为例:

f1df9cb052ce2f3cfaae678288eeb679.png

由图可以看出,最后一位的结果并不参与移位相加的运算,什么意思呢?

我们再画详细点:

3f0cd58f3941252aabfd6841ff59df02.png

计算时,每次得到的部分积,都会与上一次得到的部分积相加,而每次相加结果的最后一位不参与运算,可直接右移转存,则部分积占用的空间显然比左移运算小多了。这样说,想必应该足够明白了吧?

设计文件(方式一)

知道了上述原理,我们来写一个右移位的乘法器,逻辑很清晰,每次根据乘数位是否为0进行判断,如果是0,则部分积=前一步的部分积+0,如果是1,则部分积=前一步的部分积+被乘数,同时每次将部分积的最低位存储至结果的低位中,最后一次的部分积则为结果的高位。

实现代码如下:

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个元件,32IO口,202根线

57eade89627ecb2764aa648ee664ea85.png

设计文件(方式二)

所幸八位的乘法器也并不算复杂,可以写一个并行计算的部分积。这样也就解决了for循环的问题。

`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个元件,32IO口,224根线,和使用for循环的方式相比,多了几根线??!

好吧果然是我的能力问题,有见解的朋友们请务必说一下,我想学!

8a816d7f30a4a850f900b57909ca719b.png

测试文件

由于两个都是乘法器,输入和输出端口都一样,则可以用同样的测试文件进行测试:

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=497x8=56,结果正确。

24d97736d51070a2acad6928690c6b67.png
  • 0
    点赞
  • 0
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页