基于减法操作除法器的算法---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;

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;
            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

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;

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_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代码

猜你喜欢

转载自blog.csdn.net/alangaixiaoxiao/article/details/81624946