1. 程式人生 > >基於減法操作除法器的演算法---Verilog實現

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

引言

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


2.1 實現演算法

基於減法的除法器的演算法:
        對於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位後左右兩邊分別就是餘數和商。

畫個簡單的圖:



2.2 時序邏輯verilog HDL程式碼

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; [email protected](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 [email protected](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 [email protected](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; //======================================================= [email protected](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; end else begin temp_a <= 16'h0; temp_b <= 16'h0; end end [email protected](posedge I_clk) begin div_start_d1 <= div_start; end assign div_start_neg = div_start_d1 & (~div_start); [email protected](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

testbench程式碼

module tb_div_timing_logic(

    );
reg               I_clk;
reg               I_rst_p;
reg               I_data_valid;
reg       [7:0]   I_data_a;
reg       [7:0]   I_data_b;
wire              O_data_valid;
wire      [7:0]    O_data_shang;
wire      [7:0]    O_data_yushu;



div_timing_logic div_timing_logic_inst(
       .I_clk(I_clk),
       .I_rst_p(I_rst_p),
       .I_data_valid(I_data_valid),
       .I_data_a(I_data_a),
       .I_data_b(I_data_b),
       .O_data_valid(O_data_valid),
       .O_data_shang(O_data_shang),
       .O_data_yushu(O_data_yushu)
);

always #5  I_clk <= ~I_clk;

initial begin
   I_clk  = 0;
   I_rst_p = 1;
   I_data_valid = 0;
   I_data_a = 0;
   I_data_b = 0;
   #10;
   I_rst_p = 0;
   #30;
   @(posedge I_clk ) 
      begin 
         I_data_valid = 1;
         I_data_a = {$random}%256; 
         I_data_b = {$random}%20;
      end
   repeat(18) @(posedge I_clk)    I_data_valid = 0;
   @(posedge I_clk ) 
      begin 
         I_data_valid = 1;
         I_data_a = {$random}%256; 
         I_data_b = {$random}%20;
      end
   repeat(18) @(posedge I_clk)    I_data_valid = 0;
   @(posedge I_clk ) 
      begin 
         I_data_valid = 1;
         I_data_a = {$random}%256; 
         I_data_b = {$random}%20;
      end
   repeat(10) @(posedge I_clk)    I_data_valid = 0;      
   @(posedge I_clk ) 
      begin 
         I_data_valid = 1;
         I_data_a = {$random}%256; 
         I_data_b = {$random}%20;
      end
   repeat(18) @(posedge I_clk)    I_data_valid = 0;;   

end

endmodule

模擬:


這裡寫圖片描述

2.2 組合邏輯verilog HDL程式碼

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;

[email protected](*)
   begin
      tempa = a;
      tempb = b;
   end

[email protected](*)
   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_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

testbench程式碼

module div_combinatory_logic_tb;

reg [7:0] a;
reg [7:0] b;
wire [7:0] y_shang;
wire [7:0] y_yushu;

initial begin
    #10 
     a = {$random}%256;
     b = {$random}%20;

    #100 
     a = {$random}%256;
    b = {$random}%20;

    #100
     a = {$random}%256;
    b = {$random}%20;

    #100;
    a = 8'h8;
    b = 8'h10;

    #1000 
    $stop;
end

div_combinatory_logic div_inst
(
      .a (a),
      .b (b),
      .y_shang (y_shang),
      .y_yushu (y_yushu)
  );

  endmodule

模擬:
這裡寫圖片描述

引言

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


2.1 實現演算法

基於減法的除法器的演算法:
        對於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位後左右兩邊分別就是餘數和商。

畫個簡單的圖:



2.2 時序邏輯verilog HDL程式碼