设计需求
设计一个32bit浮点的加法器,out = A + B,假设AB均为无符号位,或者换个说法都为正数。
clk为系统时钟;rst_n为系统复位,低有效;en信号控制数据输入;busy指示模块工作状态,busy拉高时,输入无效;aIn和bIn是数据输入,out_vld,指示输出数据有效。
设计的信号列表如下:
module float_adder(inputclk,inputrst_n,inputen,input[31:0] aIn,input[31:0] bIn, outputregbusy, outputregout_vld, outputreg[31:0]out);
32bit的浮点格式
EE标准754规定了三种浮点数格式:单精度、双精度、扩展精度。前两者正好对应C语言里头的float、double或者FORTRAN里头的real、double精度类型。本文设计实现的为单精度。
单精度格式
单精度:N共32位,其中S占1位,E占8位,M占23位。
双精度格式
双精度:N共64位,其中S占1位,E占11位,M占52位。
浮点数的加法过程
运算过程:对阶、尾数求和、规格化、舍入、溢出判断
对阶:
和定点数不相同的是,浮点数的指数量级不一定是一样的,所以这也就意味着,尾数直接进行加法运算时会存在问题,也就需要首先对阶数进行处理。该过程有点像科学计数法的加法处理,把科学计数法的指数化为一致,求出来指数相差多少,然后移位处理后再进行加法减法。所以这里处理也要先求阶差。
如果把阶码大的向阶码小的看齐,就要把阶码大的数的尾数部分左移,阶码减小。这个操作有可能在移位过程中把尾数的高位部分移掉,这样就引发了数据的错误,所以,尾数左移在计算机运算中不可取。
如果把阶码小的向阶码大的看齐,在移位过程中如果发生数据丢失,也是最右边的数据位发生丢失,最右边的数据位丢失,只会影响数据的精度,不会影响数据的大小。
尾数求和:
这里就是常规的补码加法。
规格化:
右规(尾数的绝对值太大时,右规) 尾数右移一位,阶码加1。当尾数溢出( >1 )时,需要右规。是否溢出,可以通过两位的符号位得出:即尾数出现01.xx…xx或10.xx…xx(两位符号位不同)
提高浮点数的表示精度,这里设计考虑比较简单,我只考虑了同号数据相加的情况,所以这里只设计了右规的情况,不考虑符号位。
舍入判断:
这里直接用截断处理的方式,针对数据相加上溢的情况,规定了运算后上溢后将数据规定为最大值。
实现代码
module float_adder( input clk, input rst_n, input en, input [31:0] aIn, input [31:0] bIn, output reg busy, output reg out_vld, output reg [31:0]out); //运算过程:对阶、尾数求和、规格化、舍入、溢出判断 //分离阶数、尾数 wire signal_bit = aIn[31]; wire [7:0] pow_a = aIn[30:23]; wire [7:0] pow_b = bIn[30:23]; wire [22:0] val_a = aIn[22:0]; wire [22:0] val_b = bIn[22:0]; //找到输入指数阶数较大,和阶数差 //对阶:在计算机中,采用小阶向大阶看齐的方法,实现对阶。即右移 reg [22:0] pow_max ; reg [23:0] pow_dif ; reg [22:0] val_max ; reg [22:0] val_min ; reg en_dly0; always @(posedge clkornegedge rst_n)beginif(rst_n==0)beginpow_max <='d0; val_max <='d0; val_min <='d0; pow_dif <='d0; en_dly0 <='d0;endelseif( en ==1&& busy ==0)beginif(pow_a >= pow_b)beginpow_max <= pow_a; val_max <= val_a; val_min <= val_b; en_dly0 <='d1;if( pow_a - pow_b >'d23)beginpow_dif <='d23;endelsebeginpow_dif <= pow_a - pow_b;endendelsebeginpow_max <= pow_b; val_max <= val_b; val_min <= val_a; en_dly0 <='d1;if( pow_b - pow_a >'d23)beginpow_dif <='d23;endelsebeginpow_dif <= pow_b - pow_a;endendendelsebeginpow_max <= pow_max; val_max <= val_max; val_min <= val_min; pow_dif <= pow_dif; en_dly0 <='d0;endend//移位忙指示信号 reg shift_busy; reg [4:0] shift_cnt; always @(posedge clkornegedge rst_n)beginif(rst_n==0)beginshift_busy<='d0;endelseif(en_dly0 ==1)beginshift_busy <='d1;endelseif(shift_cnt == pow_dif)beginshift_busy <=0;endend//移位计数 always @(posedge clkornegedge rst_n)beginif(rst_n ==0)beginshift_cnt <='d0;endelseif(shift_busy ==1)beginif(shift_cnt == pow_dif)beginshift_cnt <= shift_cnt;endelsebeginshift_cnt <= shift_cnt +1'b1;endendelsebeginshift_cnt <='d0;endend