八位“Booth二位乘算法”乘法器

八位“Booth二位乘算法”乘法器

原理

补码乘法器

之前介绍了几篇无符号乘法器或加法器的写法,当然,稍作修改也就可以改成符合有符号数的乘法器或加法器。

但是呢,我们之前写的乘法器或加法器,其实都是默认是正数来写的,而且是以正数的原码来写的,所以上面说稍作修改也就可以成为有符号数的乘法器或加法器,其实就是对我们以为的原码进行取补码,再进行乘法或加法的运算。

随着计算机硬件部件的升级,处理器技术的发展,现代处理器中的定点数(小数点位置固定)都是按照补码形式来存储的。

所以在之前写的无符号加法器中,只要利用:
X 补 + Y 补 = [ X + Y ] 补 X_补+Y_补=[X+Y]_补 X+Y=[X+Y]
就可以轻易将原先的加法器改写成有符号加法器——只要对结果再取一次补码即可。

但是乘法器呢?稍作学习可以知道,补码的乘法是这样的:
X ∗ Y 补 = [ X ∗ Y ] 补 X*Y_补=[X*Y]_补 XY=[XY]
我们再考虑一下之前所说的:在现代处理器中的定点数都是按照补码形式来存储的

所以我们要想得到两个数的乘法结果,首先应该知道被乘数的原码和补码,再对最终结果取补码,即可得到我们期望的乘法结果。

那么如何求“X*Y补”呢?在处理器中,一个二进制数Y补形如y7y6y5y4y3y2y1y0,也就是表示一个数的补码,那么它的原码是多少呢?

补码的计算方法,除了“首位不变,余位取反再加一”的方式,还有一种就是“用溢出条件来减这个数”,在我们之前第一节课说二进制的时候,以钟表为例——“十二进制”,得到结论——“4-8的补码”。

我们用第二种取补码的方式:-8的补码=12-8=4(这里没有考虑符号问题,只是求了补码的值)

所以考虑一下符号的话,-8的补码=8-12=-4

同理:

十进制下,-4的补码=4-10=-6

二进制下,-101补码=1101补码=101-1000=-011=1011

这样解决求补码的方式在接下来的计算方面就更方便了,至于正数嘛,不变就好了。

回到上面的问题,一个二进制数Y补形如y7y6y5y4y3y2y1y0它的原码是多少呢?根据:
[ X 补 ] 补 = X [X_补]_补=X [X]=X
Y补的原码Y应该为:
Y = ( y 7 ∗ 2 7 + y 6 ∗ 2 6 + y 5 ∗ 2 5 + … … + y 0 ∗ 2 0 ) − 1 ∗ 2 8 Y=(y_7*2^7+y_6*2^6+y_5*2^5+……+y_0*2^0)-1*2^8 Y=(y727+y626+y525++y020)128
稍微化简一下:
Y = − y 7 ∗ 2 7 + ( y 6 ∗ 2 6 + y 5 ∗ 2 5 + … … + y 0 ∗ 2 0 ) Y=-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0) Y=y727+(y626+y525++y020)
所以我们如果想求X*Y,可以先求其补码:
[ X ∗ Y ] 补 = [ X ∗ ( − y 7 ∗ 2 7 ) + X ∗ ( y 6 ∗ 2 6 + y 5 ∗ 2 5 + … … + y 0 ∗ 2 0 ) ] 补 [X*Y]_补=[X*(-y_7*2^7)+X*(y_6*2^6+y_5*2^5+……+y_0*2^0)]_补 [XY]=[X(y727)+X(y626+y525++y020)]
根据补码加法“X补+Y补=[X+Y]补”再稍微化简一下:
[ X ∗ Y ] 补 = − y 7 ∗ [ X ∗ 2 7 ] 补 + y 6 ∗ [ X ∗ 2 6 ] 补 + y 5 ∗ [ X ∗ 2 5 ] 补 + … … + y 0 ∗ [ X ∗ 2 0 ] 补 [X*Y]_补=-y_7*[X*2^7]_补+y_6*[X*2^6]_补+y_5*[X*2^5]_补+……+y_0*[X*2^0]_补 [XY]=y7[X27]+y6[X26]+y5[X25]++y0[X20]
再引入一个定理:
[ X ∗ 2 n ] 补 = X 补 ∗ 2 n [X*2^n]_补=X_补*2^n [X2n]=X2n
所以上式又可以换一种写法:
[ X ∗ Y ] 补 = X 补 ∗ ( − y 7 ∗ 2 7 + ( y 6 ∗ 2 6 + y 5 ∗ 2 5 + … … + y 0 ∗ 2 0 ) ) = Y ∗ X 补 [X*Y]_补=X_补*(-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0))=Y*X_补 [XY]=X(y727+(y626+y525++y020))=YX
哦这不就是上面介绍过的补码乘法嘛:
[ X ∗ Y ] 补 = Y ∗ X 补 = X ∗ Y 补 [X*Y]_补=Y*X_补=X*Y_补 [XY]=YX=XY
如果令一个数Y1补=y6y6y5y4y3y2y1y0,去掉了首位,那么上式是不是可以理解为:
[ X ∗ Y ] 补 = X 补 ∗ Y 1 补 − y 7 ∗ X 补 ∗ 2 7 [X*Y]_补=X_补*Y1_补-y_7*X_补*2^7 [XY]=XY1y7X27
其中的Y1补不就刚好是Y补的后7位嘛?也就是说一个乘法可以分为两部分理解:首位的乘法和其他位的乘法。首位的乘法产生的部分积符号是减,其他位的部分积符号为加

经过上面的推导大家应该会对补码乘法的原理有了一定的概念,我们来把它写成竖式的形式,以(-6)x(-7)为例,原码乘应该是1110x1111,在计算机中是以补码的形式存储,所以补码乘是1010x1001,代入公式,令X补=1010Y补=1001,其运算过程如下:

这里可能有一些迷惑的是:为什么第一步运算得到的结果是11111010?为什么要在前面填充1111

这也就是所谓的符号填充,我们之前的设计中都没有涉及到符号位,所以默认都是填充0,现在遇到了负数问题,也就需要填充符号了,但是这样看起来是不是一点都觉得很奇怪?如果没办法理解的话,我建议你可以尝试对它求补码,看看是不是可以保持首位符号位不变,余位取反加一。惊叹于设计师的机智。

补码乘法器的原理讲明白了,具体电路实现的话,大家可以尝试一下,本节重点不在于此。

Booth一位乘

在上面已经讨论了补码乘法器的原理,那么什么是Booth乘法器呢?Booth乘法器是由英国的Booth夫妇提出的,并没有什么特殊含义,所以我们直接快进到内容。

经过补码乘法器的推导:
[ X ∗ Y ] 补 = X 补 ∗ ( − y 7 ∗ 2 7 + ( y 6 ∗ 2 6 + y 5 ∗ 2 5 + … … + y 0 ∗ 2 0 ) ) [X*Y]_补=X_补*(-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0)) [XY]=X(y727+(y626+y525++y020))
参考中学数学:
2 n = 2 ∗ 2 n − 1 2^n=2*2^{n-1} 2n=22n1
其核心计算思想是括号里的形式,也就是**Y补的原码Y,**所以我们对括号里的内容再进行分解合并,也就是对Y分解合并。先分解:
Y = − y 7 ∗ 2 7 + ( ( 2 − 1 ) y 6 ∗ 2 6 + ( 2 − 1 ) y 5 ∗ 2 5 + … … + ( 2 − 1 ) y 0 ∗ 2 0 ) Y=-y_7*2^7+((2-1)y_6*2^6+(2-1)y_5*2^5+……+(2-1)y_0*2^0) Y=y727+((21)y626+(21)y525++(21)y020)
这样应该挺直观了吧:
Y = − y 7 ∗ 2 7 + ( y 6 ∗ 2 7 − y 6 ∗ 2 6 ) + ( y 5 ∗ 2 6 − y 5 ∗ 2 5 ) + … … + ( y 0 ∗ 2 1 − y 0 ∗ 2 0 ) Y=-y_7*2^7+(y_6*2^7-y_6*2^6)+(y_5*2^6-y_5*2^5)+……+(y_0*2^1-y_0*2^0) Y=y727+(y627y626)+(y526y525)++(y021y020)
再合并:
Y = ( y 6 − y 7 ) ∗ 2 7 + ( y 5 − y 6 ) ∗ 2 6 + ( y 4 − y 5 ) ∗ 2 5 + … … + ( 0 − y 0 ) ∗ 2 0 Y=(y_6-y_7)*2^7+(y_5-y_6)*2^6+(y_4-y_5)*2^5+……+(0-y_0)*2^0 Y=(y6y7)27+(y5y6)26+(y4y5)25++(0y0)20
最后有个0-y0的项,看起来有点不合群,所以令:
y − 1 = 0 y_{-1}=0 y1=0
代入上式,即:
Y = ( y 6 − y 7 ) ∗ 2 7 + ( y 5 − y 6 ) ∗ 2 6 + ( y 4 − y 5 ) ∗ 2 5 + … … + ( y − 1 − y 0 ) ∗ 2 0 Y=(y_6-y_7)*2^7+(y_5-y_6)*2^6+(y_4-y_5)*2^5+……+(y_{-1}-y_0)*2^0 Y=(y6y7)27+(y5y6)26+(y4y5)25++(y1y0)20
这也就是Booth一位乘算法的原理。其优点就在于不用再像补码乘法器那样,不需要专门对最后一次部分积采用补码减法

根据上式,还可以列出Booth一位乘的规则:

y(i-1)y(i)y(i-1) - y(i)操作
000加0
01-1X补
101X补
110加0

再举个例子来计算,仍以(-6)x(-7)为例,补码乘是1010x1001,列出竖式:

可是这里为什么还是有减法呢?和常规的补码乘法器相比,简直是老和尚抹洗头膏,大可不必。甚至由于每次判断两位数字,增大了电路的复杂度,那么为什么booth乘法器如此好用呢?

其实booth一位乘算法并不常用,但是booth二位乘就不一样了,通过增加一定的空间复杂度,将运算周期减为一半!

Booth二位乘

还是根据补码乘法器,我们将Y的表达式再进行变换——先分解:
Y = − 2 ∗ y 7 ∗ 2 6 + y 6 ∗ 2 6 + ( y 5 ∗ 2 6 − 2 ∗ y 5 ∗ 2 4 ) + … … + y 0 ∗ 2 0 + y − 1 ∗ 2 0 Y=-2*y_7*2^6+y_6*2^6+(y_5*2^6-2*y_5*2^4)+……+y_0*2^0+y_{-1}*2^0 Y=2y726+y626+(y5262y524)++y020+y120
再整合:
Y = ( y 5 + y 6 − 2 ∗ y 7 ) ∗ 2 6 + ( y 3 + y 4 − 2 ∗ y 5 ) ∗ 2 4 ) + … … + ( y − 1 + y 0 − 2 ∗ y 1 ) ∗ 2 0 Y=(y_5+y_6-2*y_7)*2^6+(y_3+y_4-2*y_5)*2^4)+……+(y_{-1}+y_0-2*y_1)*2^0 Y=(y5+y62y726+y3+y42y524)++(y1+y02y1)20
好了Booth二位乘算法也完事了,类比于Booth一位乘,我们也可以列出Booth二位乘的规则:

y(i-1)y(i)y(i+1)y(i-1) + y(i) - 2*y(i+1)操作
00000
0101X补
1001X补
11022*X补,即X补<<1
001-22*X补,即X补<<1
011-1X补
101-1X补
11100

再举个例子来计算,仍以(-6)x(-7)为例,补码乘是1010x1001,列出竖式:

运算周期减半了!

好了,那Booth乘法器有没有三位乘呢?可以有,但是三位的时候就会出现加3*X补2*X补可以通过左移一位得到,而3*X补就有点麻烦了,所以不再介绍,至于四位乘、八位乘,想挑战的同学可以挑战一下。

设计思路

减法变加法

首先我们来解决一个问题,如何把减法消除?我们知道,**减去一个数,等于加上这个数的相反数;减去一个数,也等于加上这个数的补码。**这个过程中的减数也默认是正数,因为正数的补码还是正数,只有正数前面加一个符号再去补码才有用。那么如上面竖式所写,减去一个负补码,就应该等于加上“这个负补码的补码的相反数”,比如上面的补码乘法器竖式,就应该变换成如下形式:

再说明一下吧:11010,就相当于加11010的补码的相反数,即加10110的相反数,即00110

所以booth一位乘算法的示例应该变成这样:

booth二位乘算法的示例应该变成这样:

vivado特性

考虑到上述减法变加法的操作后,容易总结出:减法变加法,其实就是对补码的符号位取反,也就是对减数每一位取反后再加一。

再回读一边上述的理论部分,可能你会发现,在乘法运算中,只用到了补码和**“负补码”两种概念的数字。而在vivado中(相当于在处理器中),数字默认是以补码形式存储的,即输入的乘数默认就是补码形式**,这样只需要再求出**“负补码”**即可。设X[3:0]表示一个乘数,默认是以补码形式存储,则其“负补码”:
X 负 补 码 = ! X + 1 X_{负补码}=!X + 1 X=!X+1
至于其原码:
X 原 码 = ( X [ 3 ] , ! X [ 2 : 0 ] ) + 1 X_{原码}=(X[3],!X[2:0]) + 1 X=(X[3],!X[2:0])+1
其实根本用不着。

有了以上知识储备,我们就可以写代码啦~

设计文件

//由于实力不够,没能设计成改一个数字变一个规模的程序
`define size 8
module mul_booth_signed(
    input wire [`size - 1 : 0] mul1,mul2,
    input clk,
    input wire [2:0] clk_cnt,//运算节拍,相当于状态机了,8位的话每次运算有4个拍
    output wire [2*`size - 1 : 0] res
    );

    //由于传值默认就是补码,所以只需要再计算“负补码”即可
    wire [`size - 1 : 0] bmul1,bmul2;
    assign bmul1 = (~mul1 + 1'b1) ;
    assign bmul2 = (~mul2 + 1'b1) ;//其实乘数2的负补码也没用到。
	//其实可以把状态机的开始和结束状态都写出来,我懒得写了,同学们可以尝试一下啊~
    parameter   zeroone       =   3'b00,
                twothree      =   3'b001,
                fourfive      =   3'b010,
                sixseven      =   3'b011;
    //y(i-1),y(i),y(i+1)三个数的判断寄存器,由于有多种情况,也可以看成状态机(也可以改写成状态机形式,大家自己试试吧)
    reg [2:0] temp;

    //部分积
    reg [2*`size-1 : 0] A;
	//每个节拍下把相应位置的数据传给temp寄存器
    always @ (posedge clk) begin
        case(clk_cnt)
            zeroone  : temp <= {mul2[1:0],1'b0};
            twothree : temp <= mul2[3:1];
            fourfive : temp <= mul2[5:3];
            sixseven : temp <= mul2[7:5];
            default : temp <= 0;
        endcase
    end
	
    always @(posedge clk) begin
        if (clk_cnt == 3'b100) begin//如果节拍到4就让部分积归0,此时已经完成一次计算了
            A <= 0;
        end else case (temp)
            3'b000,3'b111 :   begin//这些是从高位到低位的判断,别看反了噢
                A <= A + 0;
            end
            3'b001,3'b010 : begin//加法操作使用补码即可,倍数利用左移解决
                A <= A + ({{8{mul1[`size-1]}},mul1} << 2*(clk_cnt-1));
            end
            3'b011 : begin
                A <= A + ({{8{mul1[`size-1]}},mul1} << 2*(clk_cnt-1) + 1);
            end
            3'b100: begin//减法操作利用“负补码”改成加法操作,倍数利用左移解决
                A <= A + ({{8{bmul1[`size-1]}},bmul1} << 2*(clk_cnt-1) + 1);
            end
            3'b101,3'b110 : begin
                A <= A + ({{8{bmul1[`size-1]}},bmul1} << 2*(clk_cnt-1));
            end
            default: A <= 0;
        endcase
    end
	//当节拍到4的时候写入结果寄存器。
    assign res = (clk_cnt == 3'b100) ? A : 0;
endmodule

这是一个八位Booth二位乘算法的乘法器,至于Booth一位和Booth四位的乘法器,大家各自尝试就好。

此外在这个文件当中,我用到了clk_cnt这个寄存器,大家是不是以为我会多用一个模块用来产生clk_cnt的波形?

身为一个懒人,我直接在测试文件里写了吼吼吼~

综合电路

37个元件,36个IO口,318根线

测试文件

`timescale 1ns / 1ps
module mul_tb(
    );
    reg [7:0] mul1,mul2;
    wire [15:0] res;
    reg clk;
    wire clk_en;
    reg [2:0] clk_cnt;

    initial begin
        mul1 <= -8'd7;
        mul2 <= -8'd3;
        clk <= 0;
        clk_cnt <= 3'b0;
    end

    always # 10 clk = ~clk;
	//clk_cnt发生器,懒人版
    always @(posedge clk) begin
        clk_cnt <= clk_cnt + 1'b1;
        if (clk_cnt == 3'b100)
            clk_cnt <= 3'b00;
    end
	//每次运算结束后,让乘数变化,以便产生不同的数据用以观察
    assign clk_en = (clk_cnt == 3'b100) ? 1'b1 : 1'b0;
    always @ (posedge clk_en) begin
        mul2 <= mul2 + 1'b1;
    end

    mul_booth_signed try(.mul1(mul1),.mul2(mul2),.res(res),.clk(clk),.clk_cnt(clk_cnt));
endmodule

仿真波形

将其改成有符号十进制数形式显示,可以验证电路设计正确。

  • 16
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 16位Booth乘法器是一种用于实现二进制数乘法的电路。在Booth乘法算法中,将乘法转化为加法操作,可以有效地减少运算的次数。以下是关于16位Booth乘法器的说明: 16位Booth乘法器由三个主要组件构成:乘法器控制单元、部分积寄存器和乘法器器件。 乘法器控制单元负责控制整个乘法过程的进行。它根据被乘数和乘数的符号位,以及乘法操作的进行情况,决定是否要进行加法、减法或无操作。该控制单元还负责在乘法操作的每一个时钟周期中,根据部分积寄存器中的数据和乘数的某些位进行计算,并更新部分积寄存器中的数据。 部分积寄存器用于存储乘法操作的中间结果。它由多个触发器组成,每个触发器对应一个乘数位和相应的部分积位。在乘法操作的每个时钟周期中,根据乘法器控制单元的指令,部分积寄存器中的数据可能会被更新。 乘法器器件是负责实现乘法操作的关键部分。它包括与乘法器控制单元和部分积寄存器之间的接口,以及用于执行加法和减法操作的电路。该器件利用Booth乘法算法,通过右移和指定位相减的方式,将乘法转化为加法。具体来说,乘法器器件会对乘数的每一位进行检查,并根据乘数位的不同情况,对部分积寄存器中的数据进行加法、减法或保持不变的操作。 通过这些组件的协作,16位Booth乘法器可以高效地进行16位二进制数的乘法运算。它可以大大缩短乘法操作所需的时间,并减少硬件资源的使用。此外,Booth乘法器也具有较低的功耗和较小的面积,因此被广泛应用于数字电路设计中。 ### 回答2: 16位booth乘法器是一种用于进行二进制乘法运算的电路。它通常由多个逻辑门和触发器组成,可以在较短的时间内完成两个16位二进制数的乘法计算。 booth乘法器使用布斯算法,通过将乘数和被乘数转换为包含加减运算的乘法算式,从而加快乘法的速度。它可以将乘法运算转化为多个位的乘法和加法运算,减少了运算的复杂性。 16位booth乘法器的输入包括两个16位的二进制数,一个是乘数,一个是被乘数。它的输出为32位的结果,由两段16位的部分组成。其中,前16位为高位部分,表示乘法结果的高位;后16位为低位部分,表示乘法结果的低位。 booth乘法器的实现主要包括多个阶段的加法、右移、选择和更新。在每一个阶段,根据乘法算法的规则,通过比较乘数的当前位与上一位的值,来确定是向左移位还是向右移位,并选择相应的操作更新部分乘积。 最后,将每个阶段得到的部分乘积相加得到最终的乘法结果。由于booth乘法器的特性,它可以在较短的时间内完成乘法计算,从而提高了计算机执行乘法运算的效率。 总的来说,16位booth乘法器是一种用于进行二进制乘法运算的电路,它通过布斯算法实现乘法的加速,能够在较短的时间内完成乘法计算,并提高计算机执行乘法运算的效率。 ### 回答3: 16位Booth乘法器是一种可以用于进行16位二进制数相乘的硬件电路。它是基于Booth算法乘法器,该算法可以有效地减少乘法操作的次数,提高乘法的速度。 Booth乘法器的原理是通过将乘数转换为Booth编码,并利用编码中的特定模式来进行快速的乘法计算。具体而言,Booth乘法器将乘数划分为若干个3位编码块,每个编码块表示乘数中连续的3位。然后,根据编码块的值,选择相应的加法或减法操作来得到部分积。 16位Booth乘法器由多个基本模块组成,包括16位乘法器,16位加法器,以及16位移位器。首先,乘法器将被乘数与Booth编码的乘数块相乘,得到部分积。然后,加法器将部分积与前一位的进位相加,得到最终的部分积。最后,移位器将部分积向右移动一位,以进行下一次计算。 通过反复进行上述的乘法和移位操作,最终可以得到完整的乘法结果。Booth乘法器虽然相对于传统的乘法器需要更多的硬件资源,但它能够在较短的时间内完成乘法计算,因此在一些需要高速乘法运算的应用中得到广泛使用。 总之,16位Booth乘法器是一种能够快速进行16位二进制数乘法计算的硬件电路。它利用Booth编码和相关的算法来减少乘法操作的次数,提高乘法的速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值