欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于减法操作除法器的算法---Verilog实现

程序员文章站 2024-02-05 11:56:40
...

引言

除法器在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位后左右两边分别就是余数和商。

画个简单的图:

基于减法操作除法器的算法---Verilog实现

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;

aaa@qq.com(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

aaa@qq.com(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
aaa@qq.com(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;
//=======================================================           
aaa@qq.com(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
aaa@qq.com(posedge I_clk)
   begin
      div_start_d1 <= div_start;
   end
assign div_start_neg = div_start_d1 & (~div_start);

aaa@qq.com(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

仿真:


基于减法操作除法器的算法---Verilog实现

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;

aaa@qq.com(*)
   begin
      tempa = a;
      tempb = b;
   end

aaa@qq.com(*)
   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

仿真:
基于减法操作除法器的算法---Verilog实现