天天看點

Verilog實作-2級流水單精度浮點乘法器1.實作目标2.原理說明3.設計說明4.Verilog代碼

目錄

  • 1.實作目标
  • 2.原理說明
  • 3.設計說明
  • 4.Verilog代碼

1.實作目标

Verilog實作-2級流水單精度浮點乘法器1.實作目标2.原理說明3.設計說明4.Verilog代碼

2.原理說明

首先要懂得浮點乘法器的工作原理與如何編碼運算的過程,這裡給出兩個參考Blog.

浮點加法、減法, 乘法、除法運算: https://blog.csdn.net/xingqingly/article/details/18981671

計算機中定點數表示方法——移碼: https://blog.csdn.net/Dnesity/article/details/104531681

3.設計說明

本次設計輸入有兩個32位單精度浮點數,時鐘信号,複位信号,使能信号和round_cfg,輸出有結果以及溢出标志;

考慮到階碼可能出現溢出,采用雙符号位,将移碼轉為補碼進行運算,再轉回移碼的政策判斷溢出;

同時考慮到尾數為0的不規範的可能性,将特殊處理,将輸出全部置為0.

變量類型為reg,無符号類型,但為了實作能進行有符号類型的計算,需要對輸入的32位單精度浮點數進行提取.

4.Verilog代碼

//**
//Author:                   Luk.wj
//Create:                  2020.11.19
//Revise:                  2020.11.24
//Fuction:   Two-stage assembly line floating point multiplier with single precision
//功能:              2級流水線的單精度浮點乘法器(IEEE Standard)
//**
module two_line_float_p_mul(flout_a,flout_b,clk,en,rst,round_cfg,flout_c,overflow); 
 input[31:0] flout_a;           // 輸入的被乘數
 input[31:0] flout_b;           // 輸入的乘數
 input clk;                     // 時鐘信号
 input en;                      // 使能信号
 input rst;                     // 複位信号
 input round_cfg;               // 決定舍入的方法,0采用chopping,1采用就近舍入 
 output[31:0] flout_c;          // 輸出運算結果
 output[1:0] overflow;          // 輸出溢出标志
 reg[31:0] flout_c;
 reg[1:0] overflow;
 reg s1,s2;                     // 輸入數符号
 reg[7:0] exp1,exp2;            // 輸入階碼
 reg[23:0] man1,man2;           // 輸入尾數,多一位,把預設的‘1’加上
 reg n;                         // 左歸階碼
 reg[9:0] temp1,temp2,temp3;    // 多兩位,用于階碼的雙符号表示,判斷溢出
 reg[47:0] mul_out_p;           // 第二級邏輯運算尾數部分

 //-------‘s'為符号,'e'為階碼,'m’為尾數------------//
 //第一級邏輯輸出
 reg       one_s_out;
 reg[9:0]  one_e_out;
 reg[47:0] one_m_out;
 
 //第一級流水寄存
 reg       one_s_reg; 
 reg[9:0]  one_e_reg; 
 reg[47:0] one_m_reg;
 

 //第二級邏輯輸出
 reg[1:0]  two_f_out; //溢出
 reg[7:0]  two_e_out; 
 reg[22:0] two_m_out; 

 //第二級流水寄存
 reg       two_s_reg;
 reg[1:0]  two_f_reg; //溢出
 reg[7:0]  two_e_reg;
 reg[22:0] two_m_reg;

/*---------------提取flout_a 的符号,階碼,尾數---------------------*/
always@(*) 
 begin
   if(!rst)
     begin       //複位,初始化
       s1   = 1'b0;
       exp1 = 8'b0000_0000;
       man1 = {1'b1,23'b0};
     end
	 else if(en) 
     begin
       s1   = flout_a[31];
       exp1 = flout_a[30:23];
       man1 = {1'b1,flout_a[22:0]};
	   end
 end
 
/*---------------提取flout_b 的符号,階碼,尾數---------------------*/
always@(*) 
 begin
   if(!rst)
     begin      //複位,初始化
       s2   = 1'b0;
       exp2 = 8'b0000_0000;
       man2 = {1'b1,23'b0};
     end
   else if(en) 
     begin
       s2   = flout_b[31];
       exp2 = flout_b[30:23];
       man2 = {1'b1,flout_b[22:0]};
     end
end

/*--------------------第一級邏輯運算---------------------------------*/
//符号位
always@(*)  one_s_out = s1^s2;  //輸入符号異或

//尾數相乘
always@(*) 
 begin
  if(man1 == 24'b10000000000_0000000000000)
	  one_m_out = 48'b0;
	else if(man2 == 24'b10000000000_0000000000000)
	  one_m_out = 48'b0;
	else
	  one_m_out = man1*man2; //48位
end

//階碼相加,階碼是移碼,移碼是符号位取反的補碼
always@(*) 
//把階碼的移碼形式變為補碼形式,并且轉成雙符号位格式,00為正,11為負
 begin
   if(exp1[7] == 1)
     temp1 = {2'b00,1'b0,exp1[6:0]};
   else 
	   temp1 = {2'b11,1'b1,exp1[6:0]};
   if(exp2[7] == 1)
     temp2 = {2'b00,1'b0,exp2[6:0]};
   else
	   temp2 = {2'b11,1'b1,exp2[6:0]}; 
   one_e_out[9:0] = temp1[9:0] + temp2[9:0];        //階碼以雙符号補碼的形式相加計算
end
/*----------------------------------------------------------------*/

//第一級流水寄存
always@(posedge clk)
 begin
   one_s_reg <= one_s_out;
	 one_e_reg <= one_e_out;
	 one_m_reg <= one_m_out;
end

/*--------------------第二級邏輯運算---------------------------------*/
//尾數規範化及舍入處理,溢出判斷
always@(*) 
 begin
   if(one_m_reg == 48'b0000000000_0000000000000)
     begin
       two_m_out =  23'b0;
       n  =  1'b0;
     end  // 處理特殊值
	 else
	   begin
		  if(one_m_reg[47] == 1)
		   begin
		     n  = 1'b1;                  //左歸碼為1
			   mul_out_p = one_m_reg >> 1; //右移一位
		  end
		  else
		   begin
			  n   = 1'b0;                 //左歸碼為0
			  mul_out_p = one_m_reg;      //不需要右移
			end
		 if(round_cfg == 1)         //0采用chopping,1采用就近舍入 
		   begin
		     if(mul_out_p[22] == 1)
			    two_m_out[22:0] = mul_out_p[45:23]+1'b1;
			   else
			    two_m_out[22:0] = mul_out_p[45:23];
		   end
		  else
		 	 two_m_out[22:0] = mul_out_p[45:23];
    end
   //雙符号的定義,01為上溢,10為下溢,符号相同無溢出
   temp3 = one_e_reg[9:0] + n + 1'b1;  //加上左歸階碼,因為補碼與移碼的轉換是-128,而IEEE是-127,故加上1
	 if(temp3[9:8] == 2'b01)  
     two_f_out = 2'b01; //階碼上溢
   else if(temp3[9:8] == 2'b10)  
	  two_f_out = 2'b10; //階碼下溢
   else 
	  two_f_out = 2'b00; //無溢出
	 //輸出補碼轉回移碼
   case(temp3[7])  
     1'b1:  two_e_out = {1'b0,temp3[6:0]}; 
     1'b0:  two_e_out = {1'b1,temp3[6:0]}; 
   endcase
end
/*----------------------------------------------------------------*/

//第二級流水寄存
always@(posedge clk) 
 begin
   if(two_m_out == 0) //特殊值處理
	  begin
	     two_s_reg <= 1'b0;
		   two_e_reg <= 8'b0;
		   two_m_reg <= 23'b0;
	     two_f_reg <= 2'b00;
	  end
   else
	  begin
       two_s_reg <= one_s_reg;
       two_f_reg <= two_f_out;
       two_e_reg <= two_e_out;
       two_m_reg <= two_m_out;
	  end
end

//輸出結果
always@(*) 
 begin
	  flout_c  = {two_s_reg,two_e_reg[7:0],two_m_reg[22:0]};
	  overflow = two_f_reg;
end

endmodule

           

繼續閱讀