• 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

  • 相关阅读:
    html+css学习笔记 5[表格、表单]
    html+css学习笔记 4[定位]
    WebService基于SoapHeader实现安全认证
    jquery获取url参数
    邮件群发
    .NET指定程序集的位置
    C# 只启动一个实例完全解决方案
    利用Google API生成二维码
    redis 面试题
    python 操作 redis 集群
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/10139350.html
Copyright © 2020-2023  润新知