基於減法操作除法器的算法---Verilog實現

除法器在FPGA裏怎麼實現呢?當然不是讓用“/”和“%”實現;
在Verilog HDL語言中雖然有除的運算指令,但是除運算符中的除數必須是2的冪,因此無法實現除數爲任意整數的除法,很大程度上限制了它的使用領域。並且多數綜合工具對於除運算指令不能綜合出令人滿意的結果,有些甚至不能給予綜合。即使可以綜合,也需要比較多的資源。對於這種情況,一般使用相應的算法來實現除法,分爲兩類:基於減法操作和基於乘法操作的算法

實現算法

基於減法的除法器的算法:
        對於32的無符號除法,被除數a除以除數b,他們的商和餘數一定不會超過32位。首先將a轉換成高32位爲0,低32位爲a的temp_a把b轉換成高32位爲b,低32位爲0的temp_b。在每個週期開始時,先將temp_a左移一位,末尾補0,然後與b比較,是否大於b,是則temp_a減去temp_b將且加上1,否則繼續往下執行。上面的移位、比較和減法(視具體情況而定)要執行32次,執行結束後temp_a的高32位即爲餘數,低32位即爲商

算法推倒(非原創):
假設4bit的兩數相除 a/b,商和餘數最多隻有4位 (假設1101/0010也就是13除以2得6餘1)

我們先自己做二進制除法,則首先看a的MSB,若比除數小則看前兩位,大則減除數,然後看餘數,以此類推直到最後看到LSB;而上述算法道理一樣,a左移進前四位目的就在於從a本身的MSB開始看起,移4次則是看到LSB爲止,期間若比除數大,則減去除數,注意減完以後正是此時所剩的餘數。而商呢則加到了這個數的末尾,因爲只要比除數大,商就是1,而商0則是直接左移了,因爲會自動補0。這裏比較巧因爲商可以隨此時的a繼續左移,然後新的商會繼續加到末尾。經過比對會發現移4位後左右兩邊分別就是餘數和商。

畫個簡單的圖:

1.時序邏輯除法

module div_timing_logic(
                        input               I_clk,
                        input               I_rst_p,
                        input               I_data_valid,
                        input       [7:0]   I_data_a,
                        input       [7:0]   I_data_b,
                        output reg          O_data_valid,
                        output reg [7:0]    O_data_shang,
                        output reg [7:0]    O_data_yushu

    );
reg  [7:0]   tempa;
reg  [7:0]   tempb;
reg  [15:0]  temp_a;
reg  [15:0]  temp_b;
reg          div_start;
reg          div_start_d1;
wire         div_start_neg;
reg  [4:0]   div_cnt;

always@(posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            tempa <= 8'h0;            
            tempb <= 8'h0;            
         end
      else if(I_data_valid)
         begin
            tempa <= I_data_a;            
            tempb <= I_data_b;            
         end
      else
         begin
            tempa <= tempa;            
            tempb <= tempb;            
         end
   end

always@(posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         div_start <= 1'b0;
      else if(I_data_valid && div_start == 1'b0)
         div_start <= 1'b1;
      else if(div_cnt == 5'd16 )
         div_start <= 1'b0;
      else
          div_start <= div_start;
   end
//========================================================div_cnt
always@(posedge I_clk or posedge I_rst_p)
   if(I_rst_p)
      div_cnt <= 5'd0;
   else if(div_start)
      div_cnt <= div_cnt + 1;
   else
      div_cnt <= 5'd0;
//=======================================================           
always@(posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            temp_a <= 16'h0;
            temp_b <= 16'h0;            
         end
      else if(div_start )
         if(div_cnt == 4'd0)
            begin
               temp_a <= {8'h0,tempa};
               temp_b <= {tempb,8'h0};
            end
         else if(div_cnt[0] == 1'b1)
            begin
               temp_a <= {temp_a[14:0],1'b0};
            end
         else
            begin
               temp_a <= (temp_a[15:8] >= temp_b[15:8])?(temp_a - temp_b + 1):temp_a;//如果比temp_b大,則減去temp_b然後加1
            end
      else
         begin
            temp_a <= 16'h0;
            temp_b <= 16'h0;              
         end

   end
always@(posedge I_clk)
   begin
      div_start_d1 <= div_start;
   end
assign div_start_neg = div_start_d1 & (~div_start);//下降沿檢測

always@(posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            O_data_valid <= 1'b0;
            O_data_shang <= 1'b0;
            O_data_yushu <= 1'b0;         
         end
      else if(div_start_neg)
         begin
            O_data_valid <= 1'b1;
            O_data_shang <= temp_a[7:0];
            O_data_yushu <= temp_a[15:8];              
         end
      else
         begin
            O_data_valid <= 1'b0;
            O_data_shang <= 1'b0;
            O_data_yushu <= 1'b0;          
         end
   end
endmodule

2.純組合邏輯

module div_combinatory_logic
(
           input       [7:0]        a,//被除數
           input       [7:0]        b,//除數
           output reg  [7:0]        y_shang,//商
           output reg  [7:0]        y_yushu//餘數
);

reg [7:0] tempa;
reg [7:0] tempb;
reg [15:0] temp_a;
reg [15:0] temp_b;

integer i;

always@(*)
   begin
      tempa = a;
      tempb = b;
   end

always@(*)
   begin
      temp_a = {8'h0,tempa};
      temp_b = {tempb,8'h0};
      for(i = 0; i < 8; i = i+1)         //注意是移動8次
         begin:shift_left
//            temp_a = temp_a << 1 ;
            temp_a = {temp_a[14:0],1'b0}  ;
            if(temp_a[15:8] >= temp_b[15:8] )//temp_b的低8位都是0,所以只需要比較高八位
               temp_a = temp_a - temp_b + 1;
            else
               temp_a = temp_a;
         end
       y_shang = temp_a[7:0];//商在低位
       y_yushu = temp_a[15:8];//餘數在高位
   end


endmodule

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章