基于Booth算法的64位浮点乘法器的实现

  • 说明
  • 64位浮点乘法器架构
  • 按位乘法算法(Bitwise Multiplication Algorithm)
  • Booth算法
  • MBA算法
  • Verilog实现

说明

该文章目的在于理解如何使用Booth算法或MBA算法实现一个简单的64位浮点乘法器,为了简便,省略了很多繁琐的细节,主要为以下几点:

  1. 64位浮点数符合 IEEE754-1985 浮点运算标准,该标准的内容本文不涉及
  2. 只考虑规格化的浮点数,对于0、无穷大、无穷小等其他特殊的浮点数都不考虑
  3. 在对结果的Mantissa舍入时,其可能会影响结果的exponent的值,为了简便,浮点乘法器的舍入规则为 Round to 0,即截断,这样exponent的值不会因为Mantissa的舍入而改变
  4. 不考虑标志信号,比如溢出标志、零标志等等
  5. 本文设计的64位乘法器,设计追求简单,目的是为了理解Booth算法和MBA算法原理,其电路延迟和硬件规模都很不理想。更好的乘法器设计请参考这篇论文 32bits高速CMOS浮点乘法器设计,本文也是基于对该论文的理解而写的。

64位浮点乘法器架构

浮点数乘法的基本原理为:指数相加,尾数相乘。

  1. 指数直接相加后的结果需要再减去一个bias,对于64位浮点数来说,bias等于1023
  2. 尾数相乘后的结果可能大于2,此时需要对结果进行规整化(Normalize),注意尾数相乘的结果一定小于4,因此规整化后指数部分最多需要加1
  3. 对规整化的结果进行舍入,可能会导致进位,从而使得指数的值加1,为了简便,舍入规则采取向0舍入,即截断

按位乘法算法(Bitwise Multiplication Algorithm)

64位浮点乘法器的关键路径在于尾数的相乘,因此如何优化尾数的乘法成为优化浮点乘法器的关键所在。首先来看基础版本的乘法算法,其原理可用下面的伪代码表示

product <- 0
while multiplier != 0:if multiplier.lsb == 1product <- product + multiplicandmultiplicand <- multiplicand << 1multiplier <- multiplier >> 1

该算法本质上和我们列竖式计算十进制数乘法的过程是一样的,将乘数的每一位分别和被乘数相乘,得到部分和,将所有的部分和累加即可得到最终的结果,其可以用下面的公式来表示,其中 B=bn−1bn−2...b0B=b_{n-1}b_{n-2}...b_0B=bn−1​bn−2​...b0​,
AB=∑i=0n−12ibiAAB=\sum_{i=0}^{n-1}2^ib_{i}A AB=i=0∑n−1​2ibi​A

Booth算法

首先来看Booth算法,其原理我参考了知乎答主坤坤的回答。

对于两个使用二进制补码表示的有符号数 AAA 和 BBB,我们可以通过重写 BBB 来减少累加的次数,设 B=bn−1bn−2...b0B=b_{n-1}b_{n-2}...b_0B=bn−1​bn−2​...b0​,为了方便表示,再另 b−1=0b_{-1}=0b−1​=0,则有

那么 A⋅BA\cdot BA⋅B 则可以用下面的表达式计算,这即为Booth算法
AB=∑i=0n−12i(−bi+bi−1)AAB=\sum_{i=0}^{n-1}2^i(-b_{i}+b_{i-1})A AB=i=0∑n−1​2i(−bi​+bi−1​)A
只看上面的表达式,和按位乘法算法相比,Booth算法似乎并没有减少累加的次数,但是如果乘数 BBB 中原来存在连续多个的1,上式中就会有多个 −bi+bi−1-b_{i}+b_{i-1}−bi​+bi−1​ 的项的值为0,从而达到减少累加的次数的目的。

但是如果乘数中原来存在“01”的连续序列,则使用上面的表达式来计算乘法会增加乘数中 “1” 的数目,反而降低了乘法的运算速度。

因此,Booth算法对乘法速度的提高与乘数 BBB 的值有关,我们希望寻找一种高速且与操作数无关的编码方式,这便有了下面的修正Booth算法(Modified Booth Arithmetic,MBA)。

MBA算法

该算法还是通过重写 BBB 来减少累加的次数,设 B=b2n+1b2nb2n−1...b0B=b_{2n+1}b_{2n}b_{2n-1}...b_0B=b2n+1​b2n​b2n−1​...b0​,如果B的最高位不为 2n+12n+12n+1,可以对其进行符号拓展,另外,为了方便表示,再另 b−1=0b_{-1}=0b−1​=0,则有

那么 A⋅BA\cdot BA⋅B 则可以用下面的表达式计算,这即为MBA算法
AB=∑i=0n4i(−2b2i+1+b2i+b2i−1)AAB=\sum_{i=0}^{n}4^i(-2b_{2i+1}+b_{2i}+b_{2i-1})A AB=i=0∑n​4i(−2b2i+1​+b2i​+b2i−1​)A
可以看到,如果 BBB 为 2n+22n+22n+2 位,则MBA算法相比按位乘法算法,其累加的次数减少了几乎一半,这样不仅可以提高运算速度,还可以减少所需加法器的数量。

应当注意MBA算法中操作数均采用二进制补码表示,在加、减运算时需要进行符号扩展。

对于64位浮点数的尾数来说,上式中一共有27个项,即
AB=∑i=0264i(−2b2i+1+b2i+b2i−1)AAB=\sum_{i=0}^{26}4^i(-2b_{2i+1}+b_{2i}+b_{2i-1})A AB=i=0∑26​4i(−2b2i+1​+b2i​+b2i−1​)A
注意 b53=0,b52=1,b−1=0b_{53}=0,b_{52}=1,b_{-1}=0b53​=0,b52​=1,b−1​=0,其余位和浮点数的二进制表示中的低52位对应。

注意到 −2b2i+1+b2i+b2i−1-2b_{2i+1}+b_{2i}+b_{2i-1}−2b2i+1​+b2i​+b2i−1​ 只可能为 0,1,−1,2,−20,1,-1,2,-20,1,−1,2,−2,因此在下面的代码实现中,对于每一个项,我采用一个4位的控制信号来控制如何生成4i(−2b2i+1+b2i+b2i−1)A4^i(-2b_{2i+1}+b_{2i}+b_{2i-1})A4i(−2b2i+1​+b2i​+b2i−1​)A 的值。

ctrl[3]ctrl[3]ctrl[3] 表示 −2b2i+1+b2i+b2i−1-2b_{2i+1}+b_{2i}+b_{2i-1}−2b2i+1​+b2i​+b2i−1​ 的正负,ctrl[2:0]ctrl[2:0]ctrl[2:0] 采用独热编码,表示 −2b2i+1+b2i+b2i−1-2b_{2i+1}+b_{2i}+b_{2i-1}−2b2i+1​+b2i​+b2i−1​ 的大小

  • ctrl[2:0]=3′b100ctrl[2:0]=3'b100ctrl[2:0]=3′b100,表示其值大小为2
  • ctrl[2:0]=3′b010ctrl[2:0]=3'b010ctrl[2:0]=3′b010,表示其值大小为1
  • ctrl[2:0]=3′b001ctrl[2:0]=3'b001ctrl[2:0]=3′b001,表示其值大小为0

Verilog实现

首先是浮点乘法器的代码,如下所示

//
// Engineer: gaojiejinsi
//
// Create Date: 2022/04/15 14:43:38
// Module Name: FPM
//// Floating point multiple
module FPM(input clk, rst,input [63:0] a, b,output [63:0] mul,output OF
);localparam EXP_WIDTH=11;wire sign;
reg [11:0] exp; // exp位12位,多出一位用来辅助判断是否发生溢出
reg [51:0] mant;// 生成符号位
assign sign = a[63] ^ b[63];// 生成e_sum和e_sum_plus1
// 根据是否要进位,从这两个信号里选择一个连接到exp上
wire [11:0] e_nobias, e_sum, e_sum_plus1;
RippleCarryAdder #(.WIDTH(12)) myadder1(.a({1'b0, b[62:52]}), .b(12'b110000000000), .cin(1'b1), .sum(e_nobias));
RippleCarryAdder #(.WIDTH(12)) myadder2(.a({1'b0, a[62:52]}), .b(e_nobias), .cin(1'b0), .sum(e_sum));
RippleCarryAdder #(.WIDTH(12)) myadder3(.a(e_sum), .b(12'b000000000001), .cin(1'b0), .sum(e_sum_plus1));// 生成控制信号
// 一共有27个需要进行累加的数,每组控制信号负责如何根据输入信号b来生成一个需要累加的数
// ctrl[i*4+3:i*4]负责控制如何根据b来生成第i个需要累加的数
// ctrl[i*4+3:i*4]的生成,通过真值表获取布尔表达式并化简而得到
reg [27*4-1:0] ctrl;
integer i;
always @* beginctrl[0] = (~b[0]) & (~b[1]);ctrl[1] = b[0];ctrl[2] = (~b[0]) & b[1];ctrl[3] = b[1];for(i = 1; i < 26; i = i + 1) beginctrl[4*i+0] = ((~b[2*i+1])&(~b[2*i])&(~b[2*i-1])) | ((b[2*i+1])&(b[2*i])&(b[2*i-1]));ctrl[4*i+1] = ((b[2*i])&(~b[2*i-1])) | ((~b[2*i])&(b[2*i-1]));ctrl[4*i+2] = ((~b[2*i+1])&(b[2*i])&(b[2*i-1])) | ((b[2*i+1])&(~b[2*i])&(~b[2*i-1]));ctrl[4*i+3] = ((b[2*i+1])&(~b[2*i-1])) | ((b[2*i+1])&(~b[2*i]));endctrl[4*26+0] = 1'b0;ctrl[4*26+1] = (~b[2*26-1]);ctrl[4*26+2] = (b[2*26-1]);ctrl[4*26+3] = 1'b0;
end// 生成27个需要进行累加的数,每个数107位
wire [107*27-1:0] add_items;
wire [27-1:0] couts;
genvar j;
generatefor(j = 0; j < 27; j = j + 1) beginGenerateF gf(.ctrl(ctrl[4*j+3:4*j]), .a(a), .out(add_items[2*j+54+107*j:2*j+107*j]), .cout(couts[j]));endfor(j = 1; j < 27; j = j + 1) beginassign add_items[2*j-1+107*j:107*j] = {{(2*j){add_items[2*j+54+107*j]}}};endfor(j = 0; j < 26; j = j + 1) beginassign add_items[107*(j+1)-1:2*j+54+107*j+1] = {{(107-54-1-2*j){add_items[2*j+54+107*j]}}};end
endgenerate// 对这27个数分三次累加,每次加9个数
wire [107-1:0] psum1, psum2, psum3;
PartialSum parts1(.a(107'b0), .add_items(add_items[9*107-1:0]), .cin(couts[8:0]), .out(psum1));
PartialSum parts2(.a(psum1), .add_items(add_items[18*107-1:9*107]), .cin(couts[17:9]), .out(psum2));
PartialSum parts3(.a(psum2), .add_items(add_items[27*107-1:18*107]), .cin(couts[26:18]), .out(psum3));// 生成exp和mant
always @* begincase(psum3[105])1'b0: {exp, mant} = {e_sum, psum3[103:52]};1'b1: {exp, mant} = {e_sum_plus1, psum3[104:53]};endcase
end// 生成mul和OF输出信号
assign mul = {sign, exp[10:0], mant};
assign OF = exp[11];endmodule

下面为模块 GenerateF 的代码,其功能为根据 ctrlctrlctrl 控制信号,生成 (−2b2i+1+b2i+b2i−1)A(-2b_{2i+1}+b_{2i}+b_{2i-1})A(−2b2i+1​+b2i​+b2i−1​)A 的值

//
// Company:
// Engineer: gaojiejinsi
//
// Create Date: 2022/04/13 08:32:27
// Module Name: GenerateF
//module GenerateF(input [3:0] ctrl,input [63:0] a,output reg [54:0] out,output reg cout);reg [54:0] out_tmp;always @* begincase(ctrl[2:0])3'b001: out_tmp = 55'b0;3'b010: out_tmp = {3'b001, a[51:0]};3'b100: out_tmp = {2'b01, a[51:0], 1'b0};default: out_tmp = 55'b0;endcasecase(ctrl[3])1'b0: {out, cout} = {out_tmp, 1'b0};1'b1: {out, cout} = {~out_tmp, 1'b1};endcase
endendmodule

下面为模块 PartialSum 的代码,其功能为计算 add_terms 中的9个的数的和,再将该和与输入 a 相加得到最终的输出 out

//
// Engineer: gaojiejinsi
//
// Create Date: 2022/04/13 08:11:22
// Module Name: PartialSum
//module PartialSum(input [106:0] a,input [962:0] add_items,input [8:0] cin,input [106:0] out);
// 可以使用generate语句,懒得改了
wire [106:0] out1, out2, out3, out4, out5, out6, out7, out8;
RippleCarryAdder #(.WIDTH(107)) myadder1(.a(a), .b(add_items[107*1-1:107*0]), .cin(cin[0]), .sum(out1));
RippleCarryAdder #(.WIDTH(107)) myadder2(.a(out1), .b(add_items[107*2-1:107*1]), .cin(cin[1]), .sum(out2));
RippleCarryAdder #(.WIDTH(107)) myadder3(.a(out2), .b(add_items[107*3-1:107*2]), .cin(cin[2]), .sum(out3));
RippleCarryAdder #(.WIDTH(107)) myadder4(.a(out3), .b(add_items[107*4-1:107*3]), .cin(cin[3]), .sum(out4));
RippleCarryAdder #(.WIDTH(107)) myadder5(.a(out4), .b(add_items[107*5-1:107*4]), .cin(cin[4]), .sum(out5));
RippleCarryAdder #(.WIDTH(107)) myadder6(.a(out5), .b(add_items[107*6-1:107*5]), .cin(cin[5]), .sum(out6));
RippleCarryAdder #(.WIDTH(107)) myadder7(.a(out6), .b(add_items[107*7-1:107*6]), .cin(cin[6]), .sum(out7));
RippleCarryAdder #(.WIDTH(107)) myadder8(.a(out7), .b(add_items[107*8-1:107*7]), .cin(cin[7]), .sum(out8));
RippleCarryAdder #(.WIDTH(107)) myadder9(.a(out8), .b(add_items[107*9-1:107*8]), .cin(cin[8]), .sum(out));endmodule

下面为行波进位加法器的代码,浮点乘法器中需要实例化该模块

//
// Engineer: gaojiejinsi
//
// Create Date: 2022/04/11 11:19:01
// Module Name: RippleCarryAdder
//
//module RippleCarryAdder #(WIDTH = 32
) (input [WIDTH-1:0] a, b,input cin,output reg [WIDTH-1:0] sum,output reg cout
);integer i;
always @* beginsum = 0;cout = cin;for(i = 0; i < WIDTH; i = i + 1) beginsum[i] = ((a[i]) ^ b[i]) ^ cout;cout = (b[i]&cout) | (a[i]&cout) | (b[i]&a[i]); end
endendmodule

基于Booth算法的64位浮点乘法器的实现相关推荐

  1. VituralBox从零搭建基于CentOS 7(64位)的Kubernetes+docker集群

    VituralBox从零搭建基于CentOS 7(64位)的Kubernetes+docker集群 1. 下载CentOS 7官方minimal镜像 2. 安装VituralBox(Windows 1 ...

  2. 基于树莓派4B搭建64位树莓派系统

    一.准备环境 1. 硬件环境 电脑一台(本人用笔记本,WIN7系统) TF卡一张(存储空间32GB以上)和读卡器 树莓派4B 2. 软件环境 系统烧入软件:balenaEtcher ,其实树莓派也有自 ...

  3. [计算机组成原理] Booth算法 —— 补码一位乘法

    x * y = z 运算规则: 1.和原码一位乘法不同,补码一位乘法的符号位是参加运算的,且运算结果和所有参加运算的数都是补码形式. 2.乘数 x 取双符号位参与运算,部分积的初始值为0: 乘数 y ...

  4. 32位单精度浮点乘法器的FPGA实现

    摘 要: 采用Verilog HDL语言, 在FPGA上实现了32位单精度浮点乘法器的设计, 通过采用改进型Booth编码,和Wallace 树结构, 提高了乘法器的速度.本文使用Altera Qua ...

  5. 64位浮点数_JavaScript 浮点数运算的精度问题

    问题描述 在 JavaScript 中整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此. 所以我们在打印 1.00 这样的浮点数的结果是 1 而非 ...

  6. 64位userdata.dll丢失_有什么一劳永逸解决私钥丢失的方法吗?

    没有. 但是有一些小建议. 先听一则小新闻. 10月10 日,胡润研究院发布了2019年胡润百富榜单,又有多名区块链行业从业者再次上榜.2018年是第一次有区块链行业入榜,由比特大陆詹克团和吴忌寒占据 ...

  7. 64位处理器_快看看你的电脑是64位还是32位操作系统,处理器支持64位可升级

    昨天把系统又重装了一下,猛然发现,我的电脑可以装64位操作系统,而我一直用的是32位操作系统,因为几年前,64位操作系统缺乏驱动,市面上的软件在32位操作系统兼容性好,而这几年,硬件配置的提高,64位 ...

  8. 64位服务器采购全攻略

    当今的服务器市场,以X86指令集为基础的IA架构(PC)服务器已成为绝对的主流,而近期及未来服务器技术的发展,绝大部分都已经或将要应用于PC服务器领域. 从国内市场来看,各大服务器厂商都在不断加快推出 ...

  9. 计算机二级安装64位的还是,判断电脑适合装64位还是32位系统需要cpu支持,很多人都搞错了!...

    昨天把系统又重装了一下,猛然发现,我的电脑可以装64位操作系统,而我一直用的是32位操作系统,因为几年前,64位操作系统缺乏驱动,市面上的软件在32位(X86)操作系统兼容性好,而这几年,硬件配置的提 ...

  10. C语言布斯乘法算法,布斯Booth算法带符号位的乘法verilog语言实现booth算法

    Booth算法的推倒表示看不懂,举例说明:算法的计算过程. 求M*Q的值 M=5,Q=6 按二进制分解M和Q :M3M2M1M0×Q3Q2Q1Q0: 0110×0101 (有符号数用补码表示,最高位表 ...

最新文章

  1. Win7各版本功能对比
  2. 自己实现spring核心功能 二
  3. ABAP SUBMIT 程序时带屏幕默认值
  4. 结对项目开发-电梯调度
  5. 【LeetCode笔记】84. 柱状图中最大的矩形(字符串、单调栈)
  6. 英特尔AMD竞相为笔记本处理器添加图形功能
  7. SpringBoot启动类自动包扫描 三种方式
  8. 在线编辑fckeditor3
  9. 词霸豆豆 — 互联网时代的金山词霸
  10. 什么转换器能将excel转换成pdf
  11. CRNN——文本识别算法
  12. 穷查理宝典_《穷查理宝典》(珍藏版)
  13. 正大国际期货:水泥价格罕见急跌!两个月跌超100元/吨 会不会影响房价?
  14. Elastic:data_hot,data_warm,data_cold角色有什么用
  15. C#从文件读取 Stream
  16. ye321片库_jQuery的简约幻灯片库
  17. pig和piglet有什么区别?
  18. 虚拟机没有USB网卡选项怎么解决
  19. 手游传奇刷元宝_传奇手游如何刷元宝
  20. 【Dash搭建可视化网站】项目13:销售数据可视化大屏制作步骤详解

热门文章

  1. python选股软件编写
  2. Android开发中,如何从系统固件里提取可用的APK
  3. 三极管死区电压和导通电压的区别
  4. 三极管、MOS管 可以反向导通吗?-----可以
  5. java 使用 ehcache_Java Ehcache简单使用
  6. RPlidar学习(三)——RPlidar源代码库
  7. vue2.0项目页面字体切换为繁体
  8. [插件安装] VS插件番茄的安装
  9. python合并多个excel
  10. 【ACL2020】结果已出,录用论文抢先看!