除法的实现2

除法的实现,相对于加减乘要麻烦一些。当然目前除法主要支持无符号数除法,我们分为两类进行介绍,一类是被除数是变量,即a/b这种,一类是被除数是常量,即a/B这种。
1. 被除数常量,方法一:长除法,即根据二进制手算除法,每次将被除数左移一位,每个周期得到一位商
    比如 11/4 = 2 于 3

           1011               --->11            
     -     100                ---->4
------------------------------------
           0011              101 > 100 商最高位1, 余数 0011,将0011左移一位
           011
     -     100                011 < 100 商次高位为0, 余数为011
  最终结果上为2'b10, 余数为011
  需要注意一点,如果除数的高位为0,则需要对被除数高位补0,比如1111/001 (15/1)
  由于001的bit2和bit1为0,因此1111需要补位为001111当作被除数,进行运算
   001111
- 001                                              商的bit3为1
------------------------------------
   0001
-    001                                            商的bit2为1
-------------------------------------
     0001
-      001                                          商的bit1为1
-------------------------------------
       0001
-        001                                        商的bit0为1
---------------------------------------
         000                                         余数为0

为了实现简便,我们对被除数的扩位进行归一化,统一扩位到被除数位宽+除数位宽,得到商取低位的被除数位宽即可。


根据这个思路,Verilog代码示意如下:

module SHIFT_DIV #(
                              parameter DIVIDEND_WIDTH = 16,
                              parameter DIVISOR_WIDTH  = 8,
                              parameter QUOTIENT_WIDTH = DIVIDEND_WIDTH,
                              parameter REMAINDER_WIDTH = DIVISOR_WIDTH - 1  
                             )
                            (
                              input                                                         clk_sys,
                              input                                                         rst_sys_n,
                              input                                                         div_strt,
                              input                                                         div_clr,
                              input  [DIVIDEND_WIDTH-1 : 0]                  dividend,
                              input  [DIVISOR_WIDTH-1 : 0]                    divisor,
                              output reg                                                 div_end,
                              output reg [QUOTIENT_WIDTH -1:0]           quotient,
                              output reg [REMAINDER_WIDTH-1:0]          remainder
                             );

    localparam  DIV_CNT_WIDTH = log2(QUOTIENT_WIDTH) + 1'b1;
    localparam  LSF_REG_WIDTH = DIVIDEND_WIDTH+DIVISOR_WIDTH;
/////////////////////////////////////////////////////////////////////////////////
    reg                                         div_cnt_en;
    reg [DIV_CNT_WIDTH-1 : 0]     div_cnt;

    reg [LSF_REG_WIDTH-1 : 0]   lsf_dividend;
   
    reg [DIVISOR_WIDTH-1 + 1 : 0]     sub_dividend_divsor;
     

/////////////////////////////////////////////////////////////////////////////////
    wire [DIVISOR_WIDTH-1 : 0]   divivend_cut;


////////////////////////////////////////////////////////////////////////////////////
//Generate counter to control calculation cycle
   always @(posedge clk_sys or negedge rst_sys_n) begin
        if (rst_sys_n == 1'b0) begin
              div_cnt_en <= 1'b0;
        end
        else begin
              if ((div_clr == 1'b1) ||
                  (div_cnt >= (QUOTIENT_WIDTH))) begin
                   div_cnt_en <= 1'b0;
              end
              else if (div_strt == 1'b1)begin
                    div_cnt_en <= 1'b1;
              end
        end
  end
  always @(posedge clk_sys or negedge rst_sys_n) begin
        if (rst_sys_n == 1'b0) begin
              div_cnt <= {DIV_CNT_WIDTH{1'b0}};
        end
        else begin
              if ((div_clr == 1'b1) || (div_strt == 1'b1) ||
                  (div_cnt >= (QUOTIENT_WIDTH))) begin
                  div_cnt <= {DIV_CNT_WIDTH{1'b0}};
              end
              else if (div_cnt_en == 1'b1)begin
                    div_cnt <= div_cnt + 1'b1;
              end
        end
  end


    assign  divivend_cut = lsf_divivend[DIVIDEND_WIDTH-1 -: DIVISOR_WIDTH];

    always @(*) begin
          sub_dividend_divsor = {1'b0,divivend_cut } - {1'b0, divisor};
    end

    always @(posedge clk_sys or negedge rst_sys_n) begin
          if (rst_sys_n == 1'b0) begin
                lsf_dividend <= {LSF_REG_WIDTH{1'b0}};
          end
          else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
                lsf_dividend <= {{DIVISOR_WIDTH{1‘b0}},dividend};
          end
          else if (div_cnt_en == 1'b1) begin
                if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
                      lsf_dividend <= {lsf_dividend[LSF_REG_WIDTH-2:0],1'b0};
                end
                else begin
                     lsf_dividend <= {sub_dividend_divsor[DIVISOR_WIDTH-2:0],
                                              lsf_dividend[LSF_REG_WIDTH-DIVIDEND_WIDTH-1:0],1'b0};
                end
          end
    end


  always @(posedge clk_sys or negedge rst_sys_n) begin
          if (rst_sys_n == 1'b0) begin
                quotient <= {QUOTIENT_WIDTH{1'b0}};
          end
          else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
                quotient <= {QUOTIENT_WIDTH{1‘b0}};
          end
          else if (div_cnt_en == 1'b1) begin
                if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
                      quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b0};
                end
                else begin
                      quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b1};
                end
          end
    end

    always @(posedge clk_sys or negedge rst_sys_n) begin
          if (rst_sys_n == 1'b0) begin
                remainder <= {REMAINDER_WIDTH{1'b0}};
          end
          else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
                remainder <= {REMAINDER_WIDTH{1'b0}};
          end
          else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
                remainder <= sub_dividend_divsor[REMAINDER_WIDTH-1:0] ;
          end
    end

always @(posedge clk_sys or negedge rst_sys_n) begin
          if (rst_sys_n == 1'b0) begin
                 div_end <= 1'b0;
          end
          else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
                 div_end <= 1'b0;
          end
          else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
                 div_end <= 1'b1 ;
          end
          else begin
                div_end <= 1'b0;
          end
    end

endmodule
posted @ 2023-03-04 17:20  大块头  阅读(28)  评论(0编辑  收藏  举报