用VerilogHDL语言设计一个符合IEEE754标准的32位单精度浮点加法器。
1 背景知识
1.1 单精度浮点数
上图中S为符号位,S为1
时表示负数,S为0
时表示正数;
Fraction代表尾数,也可以说是小数点后的小数,有23位,加上前面隐含的“1.”,总共24位代表尾数,为了最大化存储范围,规定尾数中的第一个1隐去。
Exponent代表指数,为无符号数,取值范围为[1,254],**0和255表示特殊值。**也可以说是一种特殊的移码,指数的真实值是(Exponent-Bias),对于单精度浮点数Bias为127;
1.1.1 格式
IEEE 754标准定义了基本的和扩展的浮点数格式,以及一组数量有限的算术运算的规则(加、减、乘、除、平方根、求余和比较)。
非数(Not a Number,NaN)是IEEE 754标准提供的一个专门的符号,表示IEEE 754标准格式所不能表示的数。
在32位IEEE 754单精度浮点数格式中,最大指数Emax为127,最小指数Emin为-126,而不是−127 至 128。Emin-1指数(-127)用来表示浮点0,Emax+1指数(128)用来表示正/负无穷大或NaN数。
1.1.2 特点
(1)浮点数接近0时的特点,下图描述了一个指数2位,尾数为2位的浮点数系统。浮点数0表示00 000,下一个规格化的正数表示为00 100(即2^(− b)×1.00,b为偏置常数):
浮点数0附近有一块禁止区,其中的浮点数都是非规格化的,因此无法被表示为IEEE标准格式。这个数的指数和起始位都是0的区域,也可用来表示浮点数。但是这些数都是非规格化的,其精度比规格化的精度低,会导致渐进式下溢。
(2)IEEE标准规定,缺省的舍入技术应该向最近的值舍入。
(3)IEEE标准规定了4种比较结果,分别是等于、小于、大于和无序,无序用于一个操作数是NaN数的情景。
(4)IEEE标准规定了5种异常:
操作数不合法:使用了一些不合法的操作数:NaN数、无穷大数、求负数的平方根(这里不必考虑)。
除数为0(这里是加法器,所以不需要考虑)
上溢:当结果比最大浮点数还大时,处理上溢的方法有终止计算和饱和运算(用最大值作为结果)等
下溢:当结果比最小浮点数还小时,处理下溢的方法有将最小浮点数设为0或用一个小于2^Emin的非规格化数表示最小浮点数等方式处理。
结果不准确:当某个操作产生舍入错误时。
1.2 浮点运算
1.2.1 步骤
1. 对阶
a) 指数相减:要将2个指数化为相同值,先通过比较2个指数的大小求出指数差的绝对值ΔE。
b) 对阶移位: 将指数较小的操作数的尾数右移ΔE位。
2. 尾数求和
c) 尾数加减:对完成对阶移位后的操作数进行加减运算。
3. 规格化
d) 转换:当尾数相加的结果是负数时,要进行求补操作,将其转换为符号2尾数的表示方式。
e) 前导1 和前导0 的判定:判定由于减法结果产生的左移数量或由于加法结果产生的右移数量。
f) 规格化移位:对尾数加减结果进行移位,消除尾数的非有效位,使其最高位为1。
4. 舍入
g) 舍入:有限精度浮点表示需要将规格化后的尾数舍入到固定结果。由以上基本算法可见,它包含2 个全长的移位即对阶移位和规格化移位,还要包括3 个全长的有效加法,即步骤c 、d、g。由此可见,基本算法将会有很大的时延。
1.2.2 注意:
(1)因为IEEE754标准的32位单精度浮点数的指数与尾数位于位于同一个字中,所以在加法过程开始之前必须将它们分离开。
(2)如果两个指数的差大于p+1,p为尾数的位数,这里p=23,较小的数由于太小而无法影响较大的数,结果实际就等于较大的数。
(3)结果规格化时检查指数范围,以分别检测指数下溢或上溢。指数下溢会导致结果为0,而指数上溢出会造成错误。
(4)用25作为判断指数差较大是因为,25刚好讲尾数移出并多移1位,在最近舍入时,就很方便,不需要判断大于中间数或者小于中间数
(5)根据IEEE754协议:四舍五入需要一个被认为是无限精确的数字,如果有必要,将其修改以适应目的地的格式,同时在适当的情况下发出不准确的异常、下流或溢出的信号(参见7)。除非另有说明,否则每项操作都应像首先产生一个无限精度和无限范围的中间结果,然后根据本条款中的一个属性将该结果四舍五入。
1.3 舍入和截断误差
1.3.1 内容介绍
浮点运算可能引起尾数位数的相加,需要保持尾数位数不变的方法。最简单的技术叫作截断。
比如将0.1101101截断为4位尾数的结果为0.1101。截断会产生诱导误差(即误差是由施加在数上的操作计算所引起的),诱导误差是偏差的,因为截断后的数总比截断钱小。
舍入是一种更好的减少数的尾数的技术。如果舍弃的位的值大于剩余数的最低位的一半,将剩余数的最低位+1。
比如:
1.3.2 舍入机制
(1)最简单的舍入机制是截断或向0舍入。
(2)向最近的数舍入(向偶数舍入):选择距离该数最近的那个浮点数作为结果。当要舍入的数位于两个连续浮点数的正中时,IEEE舍入机制选择最低位为0的点(即向偶数舍入)
(3)向正无穷大舍入:选择正无穷大方向上最近的有效浮点数作为结果。
(4)向负无穷大舍入:选择负无穷大方向上最近的有效浮点数作为结果。
2 基本原理与设计
1、加法器的两个操作数只考虑补码表示的规格化数;
2、计算过程中,自行考虑扩展操作数位宽,保证32位精度;
3、要求支持IEEE754标准的所有四种舍入模式;
4、自行定义异常寄存器组,当计算结果出现低于正常数、无穷大、NaN、上溢、下溢等异常情况时,需要产生异常,置位相应寄存器;
2.1 定义接口和内部信号部分
2.1.1 输入信号
时钟信号clk
,复位信号rst
,输入[31:0]宽的加数x
和被加数y
,输入[1:0]宽的4种舍入模式的选择round
。其中
round=2’b00 : 向0舍入; round=2’b01 : 向最近的数舍入(向偶数舍入); round=2’b10 : 向正无穷大舍入; round=2’b11 : 向负无穷大舍入;
2.1.2 输出信号
输出[31:0]宽的结果z
,[2:0]宽的异常标志error
,溢出标志overflow
,其中
error=3’b000 : 正常情况; error=3’b001 : 输入不是规格化数;包括非规格数、NaN、正负无穷 error=3’b010 : 低于正常数(下溢);输出出现比最小的规格数小的非规格数,即指数为0,尾数不为0 error=3’b011 : 无穷大(上溢);输出出现指数全为1,尾数全为0 error=3’b100 : NaN(上溢);输出出现指数全为1,尾数不为0
overflow=2’b00 : 正常情况; overflow=2’b01 : 上溢;结果的绝对值大于规格化数所能表达的最大值。两种情况:无穷大和NaN overflow=2’b10 : 下溢;结果规格化时检查指数范围,检测指数下溢,即低于正常值
2.1.4 内部寄存器或线网
定义[24:0]宽的尾数部分m_x
,m_y
,m_z
:IEEE 754标志的32位单精度浮点数的尾数本身是23位宽的,但是再尾数相加的时候要考虑到隐含的1,并且还需要考虑进位,所以设定为25位宽的数组来存储的尾数。
定义[7:0]宽的指数部分exponent_x
,exponent_y
,exponent_z
。
定义符号部分sign_z
,sign_x
,sign_y
。
由于需要进行对偶数舍入所以这里设定[23:0]宽的尾数移出部分out_z
来存储x与y右移出尾数的部分(相对顺序不变),
设定[2:0]宽的大小标志bigger
如果exponent_x>exponent_y,则bigger=2’b01; 如果exponent_x<exponent_y,则bigger=2’b10 ; 如果exponent_x=exponent_y,则bigger=2’b00。
设定判断哪个输入信号为0的[1:0]宽寄存器zero
.默认为0,2'b10代表x为0,2'b01代表y为0。
2.2 设计思路
本电路使用的是状态机完成设计,基本是按照三段式来完成状态机的编写,即状态跳转触发器逻辑、状态转移判断组合逻辑以及时序逻辑输出,除此之外还有一些内部信号的逻辑。
2.2.1 状态转移
这里实现的是多周期的单精度浮点加法器,所以考虑到需要状态机,所以设定[2:0]宽的state
, next
分别表示当前状态和下一个状态,并且设定:
START = 3’b000(start是初始化阶段,分离指数和尾数,同检查输入是否是规格数或是否有0), EQUALEXP = 3’b001(指数对齐阶段), ADDM = 3’b010(addm是尾数相加阶段), NORMAL = 3’b011(normal是规格化尾数阶段), ROUND = 3’b100(判断溢出阶段,此阶段同时输出结果部分), WAIT = 3'b101(等待新的输入进行计算)
状态的转换always块实现了组合逻辑,对下一个时钟周期的状态next信号进行赋值。
还有一个时序逻辑always块实现状态跳转触发器逻辑,在时钟上升沿把next赋给state。
2.2.2 新数据输入标志位
使用new信号标志新的数据导入,将等待状态转换至开始状态,并进行后续计算,x,y,round信号改变都会使new信号产生一个周期的激励。
2.2.3 输出时序逻辑(状态相关运算时序逻辑)
这个状态机并不是每一个状态都有输出,大部分情况是在ROUND舍入状态时进行输出的,但是我将状态相关的运算和输出输出时序逻辑的always块写到了一起,这样能实现比较简单的状态的相关寄存器赋值,对于比较复杂的状态,比如ADDM和ROUND状态,我都另写了一个组合逻辑的always块,来保证对应状态下寄存器赋值正确。
这个输出时序逻辑主要是一个大的case语句,并且用信号state作为参数,这意味着相关的寄存器赋值在时序上是在state状态迁移出去的那个时钟上升沿实现的,输出会比状态晚一个周期。
-
STATE状态主要进行了指数和尾数的分离,计算了两个输入的指数差,并判断在输入为0或非规格数时,将相关标志位赋值。
-
EQUALEXP状态进行对阶处理。
- 当两个数的指数差的绝对值大于等于25时,就意味着尾数将会被完全移出,并且还要额外的向外移动一位,考虑到这种情况下,尾数已经无需进行加法了,同时out_z必然小于mid_z(就近舍入的话输出就为大的大的那个数,其他舍入方案只需要判断符号),所以直接跳转到ROUND状态进行舍入的步骤就可以。同时对big_delta_e等信号根据x和y的大小进行赋值。
- 当两个指数差不够大,就将尾数右移指数差的绝对值个位,将移出的尾数存储到out_z中。并且对bigger等信号根据x和y的大小进行赋值。
-
ADDM状态进行尾数的相加。
根据两个数的符号情况,对于尾数进行相加的结果赋给m_z,移出值赋给out_z,并对z的符号位sign_z进行赋值,最后也得到m_z中第1个出现1的bit位。因为相关逻辑比较复杂,相加和找bit位的逻辑都是在本always外面的组合逻辑实现的。
-
NORMAL状态进行结果规格化。
根据计算得到的尾数最高位是否为1,判断两个数相加是否发生了进位。如果发生进位,要判断是否出现了上溢情况;如果没有进位,判断是否下溢。没有出现溢出或者错误的话,就将结果尾数进行左移或者右移,实现规格化。
-
ROUND状态进行舍入,并且还需判断舍入的结果是否溢出。
在本always块外用组合逻辑计算了z_temp,并在本always块里进行是否溢出的判断。如果没有溢出,就将z_temp赋值给z,在状态变量迁移出去的上升沿进行输出。
2.2.4 尾数加组合逻辑与最高有效位获取
根据两个数的符号和大小(bigger)对两个尾数及扩展进行相应加减操作,最后赋值给m_z_temp和out_z_temp。没有将这部分逻辑写进输出时序块中主要是因为最高有效位获取需要在同周期内完成,所以要对这两个temp信号进行处理。
为了避免较长的逻辑链,就将32位的m_z_temp分成4部分分别找到每部分最高有效位进行first_one_bit赋值。
2.2.5 ROUND状态下的输出值舍入组合逻辑
这部分根据舍入模式,对z_temp赋正确的值。
当在STATE状态检测到有0时,将大数直接对z_temp赋值。
指数差距较大情况即big_delta_e不为0,根据x和y的大小、符号关系以及4种舍入模式,对z_temp赋值。
常规情况就是error和overflow都为0,根据是否有移出值,舍入模式,移出值和中间值的关系,z的尾数m_z的最后一位是否为0(即尾数是否为偶数),z的符号等条件,对z_temp赋值。
2.3 设计流程
- 了解浮点数基本知识和运算流程
- 定义模块的端口
- 编写状态机逻辑
- 考虑多种情况编写Testbench,在Modelsim对DUT进行编译、仿真测试
- 根据测试结果,迭代地进行代码调整
- 测试通过,迁移到Vivado平台做一下综合
- 成果汇总,撰写报告
3 结果验证
3.1 正常输出
-
输入数据:0.78和0.55
0.78浮点数表示:32'b00111111010001111010111000010100
0.55浮点数表示:32'b00111111000011001100110011001101
舍入方法:向最近的数舍入(向偶数舍入)
结果:ans=0.78+0.55=1.33 32'b00111111 10101010 00111101 01110001 3faa3d70
-
输入数据:50.2和1.1
50.2浮点数表示:32'h4248CCCC
1.1浮点数表示:32'h3F8CCCCC
舍入方法:向最近的数舍入(向偶数舍入)
结果:ans=50.2+0.55=51.3 424d3332
-
输入数据:6.3158350761658E-29和1.0842014616272E-19
6.3158350761658E-29浮点数表示:32'h10A0201D
1.0842014616272E-19浮点数表示:32'h1FFFFFF5
舍入方法:向最近的数舍入(向偶数舍入)
结果:ans=1.0842014616272E-19两正数相加,由于阶码相差63,所以小数可以忽略,结果与输入大数相等 1FFFFFF5
-
输入数据:6.3158350761658E-29和1.0842014616272E-19
6.3158350761658E-29浮点数表示:32'h10A0201D
1.0842014616272E-19浮点数表示:32'h1FFFFFF5
舍入方法:向正无穷大舍入
结果:ans=32'h1FFFFFF6 两正数相加,由于阶码相差63,但因为舍入模式为向无穷大舍入,所已结果为大数加1
-
输入数据:0和50.2
0浮点数表示:32'h00000000
50.2浮点数表示:32'h4248CCCC
舍入方法:向最近的数舍入(向偶数舍入)
结果:0+50.2=50.2 4248CCCC
3.2 异常情况输出
-
输入数据:32'h7F7FFFFF和32'h7F7FFFFF(最大的规格化正数)
结果:上溢+NaN
error=3’b100 : NaN
overflow=2’b01 : 上溢
-
输入数据:32'h7F000000和32'h7F000000
结果:上溢+无穷大
error=3’b011 : 无穷大
overflow=2’b01 : 上溢
-
输入数据:32'h00800010(绝对值极小的规格化正数)和32'h80800001(绝对值极小的规格化负数)
结果:下溢+低于正常数
error=3’b010 : 低于正常数
overflow=2’b10 : 下溢
-
输入数据:32'h00000003(非规格数)和32'h00000005(非规格数)
结果:非规格数输入
error=3’b001 : 非规格数输入
其他各种输入情况也也进行了测试,这里不再列举。
4 资源利用即时序情况
使用zynq7020作为目标板,使用vivado工具综合,约束在100M时钟下,能够实现时序收敛:
参考资料
浮点数加法器设计_Phoenix_ZengHao的博客-CSDN博客
代码
DUT
module floatingadder (
input clk,
input rst,
input [31:0] x,y,
input [1:0] round,
// input new,
output reg [31:0] z,
output reg [2:0] error,
output reg [1:0] overflow
);
parameter START = 3'b000, EQUALEXP = 3'b001, ADDM = 3'b010, NORMAL = 3'b011, ROUND = 3'b100, WAIT = 3'b101;
reg [24:0] m_x,m_y,m_z;
reg [24:0] m_z_temp;
reg [7:0] exponent_x,exponent_y,exponent_z;
reg sign_z;
wire sign_x,sign_y;
reg [23:0] out_z,mid_z,out_z_temp;
reg [1:0] bigger;
reg [4:0] first_one_bit;
reg [2:0] next, state;
reg [1:0] zero;
reg signed [8:0] delta_e;
reg [1:0] big_delta_e;
wire [4:0] bit4,bit3,bit2,bit1;
wire [48:0] temp;
reg [31:0] z_temp;
reg input_change,flag,flag_;
wire new;
// reg try;
//============================状态跳转触发器逻辑====================================
always@(posedge clk or negedge rst) begin
if(!rst) begin state <= WAIT;end
else begin state <= next;end
end
//============================产生1个新数据导入的flag信号====================================
always@(x,y,round,rst) begin
if(!rst) begin
input_change = 0;
end
else begin
input_change = ~input_change;
end
end
always@(posedge clk or negedge rst) begin
if(!rst) begin
flag <= 0;
flag_ <= 0;
end
else begin
flag <= input_change;
flag_ <= flag;
end
end
assign new = flag ^ flag_;
//============================ADDM状态下的尾数加组合逻辑====================================
always@(*)begin//ADDM
{m_z_temp,out_z_temp} = 0;
if(state == ADDM) begin
if(sign_x ^ sign_y)begin//异号情况
if(bigger == 2'b01)begin//x指数大于y
{m_z_temp,out_z_temp} = {m_x,24'd0} - {m_y,out_z};
end
else if(bigger == 2'b10)begin//y指数大于x
{m_z_temp,out_z_temp} = {m_y,24'd0} - {m_x,out_z};
end
else if(bigger == 2'b00)begin
m_z_temp = (m_x > m_y)?(m_x - m_y):(m_y - m_x);
end
end
else begin
m_z_temp = m_x + m_y;
end
end
end
//============================ROUND状态下的输出值舍入组合逻辑====================================
always@(*)begin//ROUND
z_temp = 0;
if(state == ROUND) begin
//------输入有0情况------
if(zero) begin
if(zero == 2'b10)begin
z_temp = {sign_y,exponent_y[7:0],m_y[22:0]};
end
else if(zero == 2'b01)begin
z_temp = {sign_x,exponent_x[7:0],m_x[22:0]};
end
end
//------指数差距较大情况------
else if(big_delta_e) begin
if(big_delta_e == 2'b01) begin//y远大于x
case(round)
2'b00:begin//向0舍入
if(sign_y ^ sign_x==1)begin//异号
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
else begin//同号
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
end
2'b01:begin//向最近的数舍入(向偶数舍入)
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
2'b10:begin//向正无穷大舍入
if(sign_x)begin//x为负
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
end
2'b11:begin//向负无穷大舍入
if(!sign_x)begin//x为正
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
end
endcase
end
else if(big_delta_e == 2'b10)begin//x远大于y
case(round)
2'b00:begin//向0舍入
if(sign_y ^ sign_x==1)begin//异号
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
else begin//同号
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
end
2'b01:begin//向最近的数舍入(向偶数舍入)
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
2'b10:begin//向正无穷大舍入
if(sign_y)begin//y为负
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
end
2'b11:begin//向负无穷大舍入
if(!sign_y)begin//y为正
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]-23'b1};
end
end
endcase
end
end
//------常规情况------
else if((error == 0) && (overflow == 0)) begin
if(out_z)begin//要考虑舍入
case(round)
2'b00:begin//向0舍入
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
2'b01:begin//向最近的数舍入(向偶数舍入)
if(out_z > mid_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
else if(out_z < mid_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else if(m_z[0] == 0)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
end
2'b10:begin//向正无穷大舍入
if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
end
2'b11:begin//向负无穷大舍入
if(sign_z)begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]+23'b1};
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
end
endcase
end
else begin
z_temp = {sign_z,exponent_z[7:0],m_z[22:0]};
end
end
end
end
//============================符号位赋值及最高有效位判断等组合逻辑====================================
assign sign_x = x[31];
assign sign_y = y[31];
assign bit4 = m_z_temp[23]?23:m_z_temp[22]?22:m_z_temp[21]?21:m_z_temp[20]?20:m_z_temp[19]?19:m_z_temp[18]?18:0;
assign bit3 = m_z_temp[17]?17:m_z_temp[16]?16:m_z_temp[15]?15:m_z_temp[14]?14:m_z_temp[13]?13:m_z_temp[12]?12:0;
assign bit2 = m_z_temp[11]?11:m_z_temp[10]?10:m_z_temp[9]?9:m_z_temp[8]?8:m_z_temp[7]?7:m_z_temp[6]?6:0;
assign bit1 = m_z_temp[5]?5:m_z_temp[4]?4:m_z_temp[3]?3:m_z_temp[2]?2:m_z_temp[1]?1:m_z_temp[0]?0:0;
assign temp = {m_z,out_z};
//============================状态转移判断组合逻辑====================================
always@(*) begin
case(state)
START:begin
if((x[30:23]==8'd255)||(y[30:23]==8'd255))begin//出现了NaN,无穷大
next = ROUND;
end
else if(x[30:23]==8'd0)begin
if(x[22:0]==0)begin//x=0
next = ROUND;
end
else begin//非归约数
next = ROUND;
end
end
else if(y[30:23]==8'd0)begin//y=0
if(y[22:0]==0)begin//y=0
next = ROUND;
end
else begin//非归约数
next = ROUND;
end
end
else begin
next = EQUALEXP;
end
end
EQUALEXP:begin
if ((delta_e <= -25) || (delta_e >= 25)) begin
next = ROUND;
end
else begin
next = ADDM;
end
end
ADDM:begin next = NORMAL;end
NORMAL:begin next = ROUND;end
ROUND:begin next = new?START:WAIT;end
WAIT:begin next = new?START:WAIT;end
default:begin next = START;end
endcase
end
//============================状态相关运算时序逻辑====================================
always@(posedge clk or negedge rst) begin
if(!rst) begin
error <= 3'b000;
zero <= 0;
overflow <= 0;
end
else begin
case(state)
START:begin
//分离指数和尾数
exponent_x <= x[30:23];
exponent_y <= y[30:23];
m_x <= {1'b0,1'b1,x[22:0]};//最高位来控制溢出 1.F
m_y <= {1'b0,1'b1,y[22:0]};
out_z <= 24'b0;
mid_z <= {1'b1,23'b0};
zero <= 0;
error <= 3'b000;
delta_e <= x[30:23] - y[30:23];
//只有阶码[1,254]和实数0是规格化数字
if((x[30:23]==8'd255)||(y[30:23]==8'd255))begin//出现了NaN,无穷大
error <= 3'b001;
end
else if(x[30:23]==8'd0)begin
if(x[22:0]==0)begin//x=0
zero <= 2'b10;
end
else begin//非归约数
error <= 3'b001;
end
end
else if(y[30:23]==8'd0)begin//y=0
if(y[22:0]==0)begin//y=0
zero <= 2'b01;
end
else begin//非归约数
error <= 3'b001;
end
end
end
EQUALEXP:begin//对阶处理
big_delta_e <= 0;
if (delta_e <= -25)begin //y远大于x
big_delta_e <= 1;
sign_z <= sign_y;
exponent_z <= exponent_y;
m_z <= m_y;
end
else if(delta_e >= 25)begin//x远大于y,24刚好移出
big_delta_e <= 2;
sign_z <= sign_x;
exponent_z <= exponent_x;
m_z <= m_x;
end
else if (delta_e > 0) begin//x指数大于y,y尾数需要右移?
{m_y,out_z} <= {m_y,out_z} >> delta_e;
exponent_y <= exponent_x;
bigger <= 2'b01;
end
else if (delta_e < 0) begin//y指数大于x,x尾数需要右移?
{m_x,out_z} <= {m_x,out_z} >> (-delta_e);
exponent_x <= exponent_y;
bigger <= 2'b10;
end
else begin
bigger <= 2'b00;
end
end
ADDM:begin
exponent_z <= exponent_x;
m_z <= m_z_temp;
out_z <= out_z_temp;
if(sign_x ^ sign_y)begin//异号情况
if(bigger == 2'b01)begin//x指数大于y
sign_z <= sign_x;
end
else if(bigger == 2'b10)begin//y指数大于x
sign_z <= sign_y;
end
else if(bigger == 2'b00)begin
sign_z <= (m_x > m_y)?sign_x:sign_y;
end
end
else begin
sign_z <= sign_x;
end
first_one_bit <= bit4?bit4:bit3?bit3:bit2?bit2:bit1?bit1:0;//m_z中第1个出现1的bit位
end
NORMAL:begin
error <= 3'b000;
overflow <= 2'b00;
if(m_z[24])begin
if(exponent_z == 254) begin
if(m_z[23:1])begin//NaN,上溢
error <= 3'b100;
overflow <= 2'b01;
end
else begin//无穷大,上溢
error <= 3'b011;
overflow <= 2'b01;
end
end
else begin
m_z <= m_z >> 1;
exponent_z <= exponent_z + 1;
end
end
else begin
if((exponent_z < (23 - first_one_bit)) ||
(exponent_z == (23 - first_one_bit) && (temp[(first_one_bit+1) +: 23] != 0)))begin//下溢
error <= 3'b010;
overflow <= 2'b10;
end
else begin
{m_z,out_z} <= {m_z,out_z} << (23 - first_one_bit);
exponent_z <= exponent_z - (23 - first_one_bit);
end
end
end
ROUND:begin
if(z_temp[30:23]==8'd255)begin//出现了NaN,无穷大
if(z_temp[22:0])begin//NaN
error <= 3'b100;
overflow <= 2'b01;
end
else begin//无穷大?
error <= 3'b011;
overflow <= 2'b01;
end
end
else if(z_temp[30:23]==8'd0)begin//
if(z_temp[22:0])begin//非归约数,下溢
error <= 3'b010;
overflow <= 2'b10;
end
end
z <= z_temp;
end
endcase
end
end
endmodule
Testbench
`timescale 1ns/1ps
module floatingadder_tb();
reg clk,rst;
reg [31:0] x,y;
reg [1:0] round;
// reg new;
wire [31:0] z;
wire [1:0] overflow;
wire [2:0] error;
floatingadder f_test(
.clk(clk),
.rst(rst),
.x(x),
.y(y),
.round(round),
// .new(new),
.z(z),
.error(error),
.overflow(overflow)
);
always #(10) clk<=~clk;
initial begin
clk=0;
rst=1'b0;
#20 rst=1'b1;
#20 x=32'b00111111010001111010111000010100;//0.78
y=32'b00111111000011001100110011001101;//0.55
round = 2'b01;//向最近的数舍入(向偶数舍入)
//ans=0.78+0.55=1.33 32'b00111111 10101010 00111101 01110001 3faa3d70
#1000
x=32'h4248CCCC;//50.2
y=32'h3F8CCCCC;//1.1
//ans=51.3 424d3332
#1000
x=32'h10A0201D;//6.3158350761658E-29
y=32'h1FFFFFF5;//1.0842014616272E-19
//ans=1.0842014616272E-19两正数相加,由于阶码相差63,所以小数可以忽略,结果与输入大数相等 1FFFFFF5
#1000
round = 2'b10;//向正无穷大舍入
x=32'h10A0201D;//6.3158350761658E-29
y=32'h1FFFFFF5;//1.0842014616272E-19
//ans=32'h1FFFFFF6 两正数相加,由于阶码相差63,但因为舍入模式为向无穷大舍入,所已结果为大数加1
#1000
round = 2'b01;
x=32'h00000000;
y=32'h4248CCCC;
//0+50.2=50.2 4248CCCC
#1000
x=32'b01000010110010000000000000000000;//100
y=32'b01000011010010000000000000000000;//200
//ans=300 32'b01000011 10010110 00000000 00000000 43960000
#1000
x=32'hBF000000;//-0.5
y=32'h3F99999A;//1.2
//ans=0.7 3f333334
#1000
x=32'hC2787DF4;//-62.123
y=32'h42C86D0E;//100.213
//ans=38.09 42185c28
#1000
x=32'h9EC0001D;//-2.032883758613E-20
y=32'h9FFFFFF5;//-1.0842014616272E-19
//ans=-1.2874898213326E-19 a017fffe
#1000
x=32'h1EA2281D;//1.7169007646178E-20
y=32'h9FFFFFF5;//-1.0842014616272E-19
//ans=-9.1251140132126E-20 9fd775ee
#1000
x=32'h9EE2281D;//-2.3945271224212E-20
y=32'h1FFFFFF5;//1.0842014616272E-19
// //ans=8.4474876554092E-20 1fc775ee
#1000
x=32'h1EE2281F;//2.3945274455386E-20
y=32'h1FFFFFF0;//1.0842011385097E-19
//ans=1.32365388306356E-19 201c44fb
#1000
x=32'h00000000;//
y=32'h9FFFFFF0;//-1.0842011385097E-19 9FFFFFF0
//验证判断0阶段功能
//异常情况
#1000
x=32'h7F7FFFFF;
y=32'h7F7FFFFF;//验证上溢+NaN error=3'b100 overflow=2'b01
#1000
x=32'h7F000000;
y=32'h7F000000;//验证上溢+无穷大 error=3'b011 overflow=2'b01
#1000
x=32'h00800010;
y=32'h80800001;//验证下溢+低于正常数 error=3'b010 overflow=2'b10
#1000
x=32'h00000003;
y=32'h00000005;//非规格数字+非规格数字 error=3'b001
#1000
x=32'h000003FF;
y=32'h3F8003FF;//非规格
#1000
x=32'h7F800003;
y=32'h7F800004;//验证上溢+NaN error=3'b100 overflow=2'b01 FFFFFFFF
#1000
x=32'h1FFFFFFF;//1.084202107862E-19
y=32'h9FFFFFF0;//-1.0842011385097E-19
//ans=0.0000009693523 15F00000
#1000
x=32'h00000003;
y=32'h00800002;//非规格数字+正常数字 error=3'b001
#1000
x=32'h1EE2281F;//2.3945274455386E-20
y=32'h1FFFFFF0;//1.0842011385097E-19
//ans=1.32365388306356E-19
#1000
x=32'h00000003;
y=32'h7F800004;//非规格数字+正常数字 error=3'b001
#1000
x=32'h1EE2281F;//2.3945274455386E-20
y=32'h1FFFFFF0;//1.0842011385097E-19
//ans=1.32365388306356E-19
#1000
x=32'h7F800000;
y=32'h00000003;//非规格数字+非规格数字 error=3'b001
#1000
x=32'h1EE2281F;//2.3945274455386E-20
y=32'h1FFFFFF0;//1.0842011385097E-19
//ans=1.32365388306356E-19
#1000
x=32'h7F800000;
y=32'h1FFFFFF0;//非规格数字+正常数字 error=3'b001
#2000 $stop;
end
endmodule
后记
这是本人第一次用Verilog写中规模电路,在写的过程中觉得使用Top-down的思路用子模块组一个加法器也是很好的方案。这个模块完成了预想功能,但逻辑写的有点复杂冗余,虽然容易理解,但无法保证电路性能最优,个人也不准备再优化。