Verilog 加法器和减法器(7)

在计算机中浮点数 表示通常采用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
View Code

下面是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

image

浮点数减法很简单,只要把减数的符号位取反就可以了。

下面是浮点加减法代码。如果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

功能仿真的波形如下:

image

原文地址:https://www.cnblogs.com/mikewolf2002/p/10139350.html