在计算机中浮点数 表示通常采用IEEE754规定的格式,具体参考以下文章。
https://www.cnblogs.com/mikewolf2002/p/10095995.html
下面我们在Verilog中用状态机实现单精度浮点数的加减法功能。这个实现是多周期的单精度浮点加法。
浮点加法分为以下几个步骤:
1.初始化阶段,分离指数和尾数以及符号位。判断加数和被加数是否是规约浮点数,不是话,直接置overflow=0x11,重新进入初始化阶段,进行下一组数的加法
2.判断加数和被加数中是否有0,有零的话,可以直接得到结果。
3.对接操作,小阶向大阶对齐。
4.对接后,进行尾数相加。
5.规格化尾数,进行左规和右规处理。
6.判断是否溢出,设置overflow标志。
下面是verilog代码:
module floatadd(clk, rst_n, x, y, z,overflow); input clk; input rst_n; input [31:0] x; input [31:0] y; output [31:0] z; output [1:0] overflow;//0,没有溢出,1,上溢,10,下溢,11 输入不是规格化数 reg [31:0] z; // z=x+y reg[24:0] xm, ym, zm; //尾数部分, 0+ 1+[22:0], reg[7:0] xe, ye, ze; //阶码部分 reg[2:0] state, nextstate; //状态机 reg zsign; //z的符号位 reg [1:0] overflow; parameter start=3'b000,zerock=3'b001,exequal=3'b010,addm=3'b011,infifl=3'b100,over =3'b110; always @(posedge clk) begin if(!rst_n) state <= start; else state <= nextstate; end //状态机进行浮点加法处理 always@(state,nextstate,xe,ye,xm,ym,ze,zm) begin case(state) start: //初始化,分离尾数和指数,调整符号位 begin xe <= x[30:23]; xm <= {1'b0,1'b1,x[22:0]}; ye <= y[30:23]; ym <= {1'b0,1'b1,y[22:0]}; //判断是否溢出,大于最大浮点数,小于最小浮点数 if((xe==8'd255)||(ye==8'd255)||((xe==8'd0)&&(xm[22:0]!=23'b0))||((ye==8'd0)&&(ym[22:0]!=23'b0)) ) begin overflow <= 2'b11; nextstate <= start; //直接到初始化 z <= 32'b1; //直接赋值最小非规约数, end else nextstate <= zerock; end zerock://检测x,y如果有一个为0,则跳转到over state begin if((x[22:0]==23'b0)&&(xe==8'b0)) begin {zsign, ze,zm} <= {y[31],ye, ym}; nextstate <= over; end else begin if((y[22:0]==23'b0)&&(ye==8'b0)) begin {zsign,ze,zm} <= {x[31],xe, xm}; nextstate <= over; end else nextstate <= exequal; end end exequal: begin if(xe == ye) nextstate <= addm; else begin if(xe > ye) begin ye <= ye + 1'b1;//阶码加1 ym[23:0] <= {1'b0, ym[23:1]}; if(ym==8'b0) begin zm <= xm; ze <= xe; zsign<=x[31]; nextstate <= over; end else nextstate <= exequal; end else begin xe <= xe + 1'b1;//阶码加1 xm[23:0] <= {1'b0, xm[23:1]}; if(xm==8'b0) begin zm <= ym; ze <= ye; zsign <= y[31]; nextstate <= over; end else nextstate <= exequal; end end end addm://尾数相加 begin ze <= xe; if((x[31]^y[31])==1'b0) //同符号 begin zsign = x[31]; zm <= xm + ym; end else begin if(xm>ym) begin zsign = x[31]; zm <= xm - ym; end else begin zsign = y[31]; zm <= ym - xm; end end if(zm[23:0]==24'b0) nextstate <= over; else nextstate <=infifl; end infifl://规格化处理 begin if(zm[24]==1'b1)//有进位,或借位 begin zm <= {1'b0,zm[24:1]}; ze <= ze + 1'b1; nextstate <= over; end else begin if(zm[23]==1'b0) begin zm <= {zm[23:0],1'b0}; ze <= ze - 1'b1; nextstate <= infifl; end else begin nextstate <= over; end end end over: begin z <= {zsign, ze[7:0], zm[22:0]}; //判断是否溢出,大于最大浮点数,小于最小浮点数 if(ze==8'd255 ) begin overflow <= 2'b01; end else if((ze==8'd0)&&(zm[22:0]!=23'b0)) //不处理非规约数 begin overflow <= 2'b10; end else overflow <= 2'b00; nextstate <= start; end default: begin nextstate <= start; end endcase end endmodule
下面是testbench代码:
代码中仅有两组加法操作,以后会写出更完备的testbench代码,用c语言产生更多的测试数据,在testbench中读入。to do…
`timescale 1ns/1ns `define clock_period 20 module floatadd_tb; reg [31:0] x,y; wire [31:0] z; reg clk; reg rst_n; wire [1:0] overflow; floatadd floatadd_0( .clk(clk), .rst_n(rst_n), .x(x), .y(y), .add(add), .z(z), .overflow(overflow) ); initial clk = 0; always #(`clock_period/2) clk = ~clk; initial begin x = 0; rst_n = 1'b0; #20 rst_n = 1'b1; #(`clock_period) x = 32'b01000000011011101001011110001101; //3.456 #(`clock_period*7) x = 32'hc2b5999a; //-90.8 end initial begin y = 0; #20 #(`clock_period) y = 32'b01000000010011001100110011001101;//2.4 #(`clock_period*7) y = 32'h41a3c28f;//20.47 end initial begin #(`clock_period*100) $stop; end endmodule
浮点数减法很简单,只要把减数的符号位取反就可以了。
下面是浮点加减法代码。如果add为1,执行加法操作,如果add为0,执行减法操作。
module floataddsub(clk, rst_n, x, y, add, z,overflow); input clk; input rst_n; input [31:0] x; input [31:0] y; input add; output [31:0] z; output [1:0] overflow; wire [31:0] y1; floatadd floatadd_0( .clk(clk), .rst_n(rst_n), .x(x), .y(y1), .z(z), .overflow(overflow) ); assign y1 = add ? y:{~y[31],y[30:0]}; endmodule
用下面的testbench代码,实现加减法操作。
`timescale 1ns/1ns `define clock_period 20 module floataddsub_tb; reg [31:0] x,y; reg add; wire [31:0] z; reg clk; reg rst_n; wire [1:0] overflow; floataddsub floataddsub_0( .clk(clk), .rst_n(rst_n), .x(x), .y(y), .add(add), .z(z), .overflow(overflow) ); initial begin clk = 1'b0; add = 1'b0; #(`clock_period*9) add = 1'b1; end always #(`clock_period/2) clk = ~clk; initial begin x = 0; rst_n = 1'b0; #20 rst_n = 1'b1; #(`clock_period) x = 32'b01000000011011101001011110001101; //3.456 #(`clock_period*7) x = 32'hc2b5999a; //-90.8 end initial begin y = 0; #20 #(`clock_period) y = 32'b01000000010011001100110011001101;//2.4 #(`clock_period*7) y = 32'h41a3c28f;//20.47 end initial begin #(`clock_period*100) $stop; end endmodule
功能仿真的波形如下: