八位“Booth二位乘算法”乘法器
文章目录
- 八位“Booth二位乘算法”乘法器
- 原理
- 补码乘法器
- Booth一位乘
- Booth二位乘
- 设计思路
- 减法变加法
- vivado特性
- 设计文件
- 综合电路
- 测试文件
- 仿真波形
八位“Booth二位乘算法”乘法器
原理
补码乘法器
之前介绍了几篇无符号乘法器或加法器的写法,当然,稍作修改也就可以改成符合有符号数的乘法器或加法器。
但是呢,我们之前写的乘法器或加法器,其实都是默认是正数来写的,而且是以正数的原码来写的,所以上面说稍作修改也就可以成为有符号数的乘法器或加法器,其实就是对我们以为的原码进行取补码,再进行乘法或加法的运算。
随着计算机硬件部件的升级,处理器技术的发展,现代处理器中的定点数(小数点位置固定)都是按照补码形式来存储的。
所以在之前写的无符号加法器中,只要利用:
X补+Y补=[X+Y]补X_补+Y_补=[X+Y]_补 X补+Y补=[X+Y]补
就可以轻易将原先的加法器改写成有符号加法器——只要对结果再取一次补码即可。
但是乘法器呢?稍作学习可以知道,补码的乘法是这样的:
X∗Y补=[X∗Y]补X*Y_补=[X*Y]_补 X∗Y补=[X∗Y]补
我们再考虑一下之前所说的:在现代处理器中的定点数都是按照补码形式来存储的。
所以我们要想得到两个数的乘法结果,首先应该知道被乘数的原码和补码,再对最终结果取补码,即可得到我们期望的乘法结果。
那么如何求“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=(y7∗27+y6∗26+y5∗25+……+y0∗20)−1∗28Y=(y_7*2^7+y_6*2^6+y_5*2^5+……+y_0*2^0)-1*2^8 Y=(y7∗27+y6∗26+y5∗25+……+y0∗20)−1∗28
稍微化简一下:
Y=−y7∗27+(y6∗26+y5∗25+……+y0∗20)Y=-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0) Y=−y7∗27+(y6∗26+y5∗25+……+y0∗20)
所以我们如果想求X*Y
,可以先求其补码:
[X∗Y]补=[X∗(−y7∗27)+X∗(y6∗26+y5∗25+……+y0∗20)]补[X*Y]_补=[X*(-y_7*2^7)+X*(y_6*2^6+y_5*2^5+……+y_0*2^0)]_补 [X∗Y]补=[X∗(−y7∗27)+X∗(y6∗26+y5∗25+……+y0∗20)]补
根据补码加法“X补+Y补=[X+Y]补
”再稍微化简一下:
[X∗Y]补=−y7∗[X∗27]补+y6∗[X∗26]补+y5∗[X∗25]补+……+y0∗[X∗20]补[X*Y]_补=-y_7*[X*2^7]_补+y_6*[X*2^6]_补+y_5*[X*2^5]_补+……+y_0*[X*2^0]_补 [X∗Y]补=−y7∗[X∗27]补+y6∗[X∗26]补+y5∗[X∗25]补+……+y0∗[X∗20]补
再引入一个定理:
[X∗2n]补=X补∗2n[X*2^n]_补=X_补*2^n [X∗2n]补=X补∗2n
所以上式又可以换一种写法:
[X∗Y]补=X补∗(−y7∗27+(y6∗26+y5∗25+……+y0∗20))=Y∗X补[X*Y]_补=X_补*(-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0))=Y*X_补 [X∗Y]补=X补∗(−y7∗27+(y6∗26+y5∗25+……+y0∗20))=Y∗X补
哦这不就是上面介绍过的补码乘法嘛:
[X∗Y]补=Y∗X补=X∗Y补[X*Y]_补=Y*X_补=X*Y_补 [X∗Y]补=Y∗X补=X∗Y补
如果令一个数Y1补=y6y6y5y4y3y2y1y0
,去掉了首位,那么上式是不是可以理解为:
[X∗Y]补=X补∗Y1补−y7∗X补∗27[X*Y]_补=X_补*Y1_补-y_7*X_补*2^7 [X∗Y]补=X补∗Y1补−y7∗X补∗27
其中的Y1补
不就刚好是Y补
的后7位嘛?也就是说一个乘法可以分为两部分理解:首位的乘法和其他位的乘法。首位的乘法产生的部分积符号是减,其他位的部分积符号为加。
经过上面的推导大家应该会对补码乘法的原理有了一定的概念,我们来把它写成竖式的形式,以(-6)x(-7)
为例,原码乘应该是1110x1111
,在计算机中是以补码的形式存储,所以补码乘是1010x1001
,代入公式,令X补=1010
,Y补=1001
,其运算过程如下:
这里可能有一些迷惑的是:为什么第一步运算得到的结果是11111010
?为什么要在前面填充1111
?
这也就是所谓的符号填充,我们之前的设计中都没有涉及到符号位,所以默认都是填充0
,现在遇到了负数问题,也就需要填充符号了,但是这样看起来是不是一点都觉得很奇怪?如果没办法理解的话,我建议你可以尝试对它求补码,看看是不是可以保持首位符号位不变,余位取反加一。惊叹于设计师的机智。
补码乘法器的原理讲明白了,具体电路实现的话,大家可以尝试一下,本节重点不在于此。
Booth一位乘
在上面已经讨论了补码乘法器的原理,那么什么是Booth
乘法器呢?Booth
乘法器是由英国的Booth
夫妇提出的,并没有什么特殊含义,所以我们直接快进到内容。
经过补码乘法器的推导:
[X∗Y]补=X补∗(−y7∗27+(y6∗26+y5∗25+……+y0∗20))[X*Y]_补=X_补*(-y_7*2^7+(y_6*2^6+y_5*2^5+……+y_0*2^0)) [X∗Y]补=X补∗(−y7∗27+(y6∗26+y5∗25+……+y0∗20))
参考中学数学:
2n=2∗2n−12^n=2*2^{n-1} 2n=2∗2n−1
其核心计算思想是括号里的形式,也就是**Y补
的原码Y
,**所以我们对括号里的内容再进行分解合并,也就是对Y
分解合并。先分解:
Y=−y7∗27+((2−1)y6∗26+(2−1)y5∗25+……+(2−1)y0∗20)Y=-y_7*2^7+((2-1)y_6*2^6+(2-1)y_5*2^5+……+(2-1)y_0*2^0) Y=−y7∗27+((2−1)y6∗26+(2−1)y5∗25+……+(2−1)y0∗20)
这样应该挺直观了吧:
Y=−y7∗27+(y6∗27−y6∗26)+(y5∗26−y5∗25)+……+(y0∗21−y0∗20)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=−y7∗27+(y6∗27−y6∗26)+(y5∗26−y5∗25)+……+(y0∗21−y0∗20)
再合并:
Y=(y6−y7)∗27+(y5−y6)∗26+(y4−y5)∗25+……+(0−y0)∗20Y=(y_6-y_7)*2^7+(y_5-y_6)*2^6+(y_4-y_5)*2^5+……+(0-y_0)*2^0 Y=(y6−y7)∗27+(y5−y6)∗26+(y4−y5)∗25+……+(0−y0)∗20
最后有个0-y0
的项,看起来有点不合群,所以令:
y−1=0y_{-1}=0 y−1=0
代入上式,即:
Y=(y6−y7)∗27+(y5−y6)∗26+(y4−y5)∗25+……+(y−1−y0)∗20Y=(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=(y6−y7)∗27+(y5−y6)∗26+(y4−y5)∗25+……+(y−1−y0)∗20
这也就是Booth
一位乘算法的原理。其优点就在于不用再像补码乘法器那样,不需要专门对最后一次部分积采用补码减法。
根据上式,还可以列出Booth
一位乘的规则:
y(i-1) | y(i) | y(i-1) - y(i) | 操作 |
---|---|---|---|
0 | 0 | 0 | 加0 |
0 | 1 | -1 |
减X补
|
1 | 0 | 1 |
加X补
|
1 | 1 | 0 | 加0 |
再举个例子来计算,仍以(-6)x(-7)
为例,补码乘是1010x1001
,列出竖式:
可是这里为什么还是有减法呢?和常规的补码乘法器相比,简直是老和尚抹洗头膏,大可不必。甚至由于每次判断两位数字,增大了电路的复杂度,那么为什么booth乘法器如此好用呢?
其实booth
一位乘算法并不常用,但是booth二位乘就不一样了,通过增加一定的空间复杂度,将运算周期减为一半!
Booth二位乘
还是根据补码乘法器,我们将Y
的表达式再进行变换——先分解:
Y=−2∗y7∗26+y6∗26+(y5∗26−2∗y5∗24)+……+y0∗20+y−1∗20Y=-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∗y7∗26+y6∗26+(y5∗26−2∗y5∗24)+……+y0∗20+y−1∗20
再整合:
Y=(y5+y6−2∗y7)∗26+(y3+y4−2∗y5)∗24)+……+(y−1+y0−2∗y1)∗20Y=(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+y6−2∗y7)∗26+(y3+y4−2∗y5)∗24)+……+(y−1+y0−2∗y1)∗20
好了Booth
二位乘算法也完事了,类比于Booth
一位乘,我们也可以列出Booth
二位乘的规则:
y(i-1) | y(i) | y(i+1) | y(i-1) + y(i) - 2*y(i+1) | 操作 |
---|---|---|---|---|
0 | 0 | 0 | 0 |
加0
|
0 | 1 | 0 | 1 |
加X补
|
1 | 0 | 0 | 1 |
加X补
|
1 | 1 | 0 | 2 |
加2*X补 ,即X补<<1
|
0 | 0 | 1 | -2 |
减2*X补 ,即X补<<1
|
0 | 1 | 1 | -1 |
减X补
|
1 | 0 | 1 | -1 |
减X补
|
1 | 1 | 1 | 0 |
加0
|
再举个例子来计算,仍以(-6)x(-7)
为例,补码乘是1010x1001
,列出竖式:
运算周期减半了!
好了,那Booth
乘法器有没有三位乘呢?可以有,但是三位的时候就会出现加3*X补
,2*X补
可以通过左移一位得到,而3*X补
就有点麻烦了,所以不再介绍,至于四位乘、八位乘,想挑战的同学可以挑战一下。
设计思路
减法变加法
首先我们来解决一个问题,如何把减法消除?我们知道,**减去一个数,等于加上这个数的相反数;减去一个数,也等于加上这个数的补码。**这个过程中的减数也默认是正数,因为正数的补码还是正数,只有正数前面加一个符号再去补码才有用。那么如上面竖式所写,减去一个负补码,就应该等于加上“这个负补码的补码的相反数”,比如上面的补码乘法器竖式,就应该变换成如下形式:
再说明一下吧:减11010
,就相当于加11010
的补码的相反数,即加10110
的相反数,即00110
。
所以booth
一位乘算法的示例应该变成这样:
booth
二位乘算法的示例应该变成这样:
vivado特性
考虑到上述减法变加法的操作后,容易总结出:减法变加法,其实就是对补码的符号位取反,也就是对减数每一位取反后再加一。
再回读一边上述的理论部分,可能你会发现,在乘法运算中,只用到了补码和**“负补码”两种概念的数字。而在vivado
中(相当于在处理器中),数字默认是以补码形式存储的,即输入的乘数默认就是补码形式**,这样只需要再求出**“负补码”**即可。设X[3:0]
表示一个乘数,默认是以补码形式存储,则其“负补码”:
X负补码=!X+1X_{负补码}=!X + 1 X负补码=!X+1
至于其原码:
X原码=(X[3],!X[2:0])+1X_{原码}=(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) begincase(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;endcaseendalways @(posedge clk) beginif (clk_cnt == 3'b100) begin//如果节拍到4就让部分积归0,此时已经完成一次计算了A <= 0;end else case (temp)3'b000,3'b111 : begin//这些是从高位到低位的判断,别看反了噢A <= A + 0;end3'b001,3'b010 : begin//加法操作使用补码即可,倍数利用左移解决A <= A + ({{8{mul1[`size-1]}},mul1} << 2*(clk_cnt-1));end3'b011 : beginA <= A + ({{8{mul1[`size-1]}},mul1} << 2*(clk_cnt-1) + 1);end3'b100: begin//减法操作利用“负补码”改成加法操作,倍数利用左移解决A <= A + ({{8{bmul1[`size-1]}},bmul1} << 2*(clk_cnt-1) + 1);end3'b101,3'b110 : beginA <= A + ({{8{bmul1[`size-1]}},bmul1} << 2*(clk_cnt-1));enddefault: A <= 0;endcaseend//当节拍到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 beginmul1 <= -8'd7;mul2 <= -8'd3;clk <= 0;clk_cnt <= 3'b0;endalways # 10 clk = ~clk;//clk_cnt发生器,懒人版always @(posedge clk) beginclk_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) beginmul2 <= mul2 + 1'b1;endmul_booth_signed try(.mul1(mul1),.mul2(mul2),.res(res),.clk(clk),.clk_cnt(clk_cnt));
endmodule
仿真波形
将其改成有符号十进制数形式显示,可以验证电路设计正确。
八位“Booth二位乘算法”乘法器相关推荐
- FPGA学习之路—应用程序—原码二位乘法器及Verilog代码分析
FPGA学习之路--原码二位乘法器及Verilog代码分析 原理 原码乘法可以分为原码一位乘和原码二位乘,两者在实现规则上大同小异.原码一位乘每次判断乘数的最低位,对被乘数和部分积进行相应操作.而原码 ...
- java大数运算详解【其三】大数乘法之平方算法之按位二次展开式算法
目录 java大数运算详解[其一]大数加减法 java大数运算详解[其二]大数乘法 java大数运算详解[其三]大数乘法之平方算法之按位二次展开式算法 java大数运算详解[其四]大数乘法之平方算法之 ...
- 32位单精度浮点乘法器的FPGA实现
摘 要: 采用Verilog HDL语言, 在FPGA上实现了32位单精度浮点乘法器的设计, 通过采用改进型Booth编码,和Wallace 树结构, 提高了乘法器的速度.本文使用Altera Qua ...
- 算法系列(二):贪心算法--Huffman编码
算法系列(二):贪心算法--Huffman编码 一.分析 问题描述: 哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法.其压缩率通常在20%-90%之间.哈夫曼编码算法使用字符在文件中出现的频率 ...
- 《算法技术手册》一2.4.6 二次方的算法性能
2.4.6 二次方的算法性能 现在考虑一个类似的问题:两个n位的整数相乘.例2-4展示了使用小学课堂上学过的算法实现的乘法运算,其中n位数字的表示方法与之前的加法一样. 例2-4:mult乘法的Jav ...
- 基于特征选择的局部敏感哈希位选择算法
点击上方蓝字关注我们 基于特征选择的局部敏感哈希位选择算法 周文桦, 刘华文, 李恩慧 浙江师范大学数学与计算机科学学院,浙江 金华 321001 摘要:作为主流的信息检索方法,局部敏感哈希往往需要生 ...
- 我有一个梦想,希望每一位提到算法的人,不再立即紧皱眉头
点击关注 异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书--异步小编 多年来,我有一个梦想,希望每一位提到算法的人,不再立即紧皱眉头,脑海闪现枯燥 ...
- 一位原码乘法器 一位补码乘法器原理
一位原码乘法器 原文链接(可能需要翻墙) 在定点计算机中,原码表示的两个数字相乘的计算规则是:乘积的符号位由两个数字的符号的异或运算得到,乘积的数字部分是两个正数的乘积.设n位被乘数和乘数用定点小数表 ...
- 身份证号码含义及最后一位校验算法(ISO 7064:1983.MOD 11-2)
身份证的各位数字代表的含义 ISO 7064:1983.MOD11-2校验码计算法 : (身份证校验码-第18位) 校验码计算(Python 代码) 身份证的各位数字代表的含义 1.前1.2位数字表示 ...
最新文章
- 灰帽黑客:正义黑客的道德规范、渗透测试、攻击方法和漏洞分析技术(第3版)
- POJ - 2018 二分+单调子段和
- vue error:The template root requires exactly one element.
- java 拦截器ajax_(转)拦截器深入实践 - JAVA XML JAVASCRIPT AJAX CSS - BlogJava
- Ubuntu 16.04 LTS与windows双系统时间同步解决方法
- 【技术分享】几维安全CTO刘柏江:IoT时代LLVM编译器防护的艺术
- Fortran入门教程(二)——数据类型
- 利用极域电子教室控制别人电脑
- 创业案例:如何调整股权,才不伤害合伙人感情?
- 财富提升成都IT产业吸引力
- 中山大学农学院袁超磊课题组博士后招聘
- AltiumDesigner中如何将原理图导成黑白色图
- 人生没有白走的路,每一步都算数
- 简历中的项目经历怎么写?
- Could not contact [localhost:8005] (base port [8005] and offset [0]). Tomcat may not be running.
- Word文档重新打开时恢复到上次阅读位置
- 【Unity-UGUI控件全面解析】| Dropdown 下拉菜单组件详解
- docker进入容器出现bash-4.2#解决办法
- 智慧图书馆中一般有哪些设备
- 三星苹果盛极而衰,国产手机迎来分化
热门文章
- C++和opencv实现图像分割(二)
- NavicatforMySQL_繁星漫天_新浪博客
- Python 批量化新建文件夹
- 事件冒泡和事件捕获的区别
- 用jk触发器构成二分频电路_如何用下沿触发JK触发器设计一个同步二,四分频电路?...
- 基于ssm+vue的师生防疫登记管理系统 elementui
- 海光国产CPU芯片和服务器,海光CPU芯片 一文看懂国产CPU!“造不如买”时代终
- java ssi_快速部署SSI框架
- 激活函数ReLU、Leaky ReLU、PReLU和RReLU
- BI领导驾驶舱-企业管理者不可或缺的决策帮手