• 【Verilog 教程】7.1Verilog 除法器设计


    除法器原理(定点)
    和十进制除法类似,计算 27 除以 5 的过程如下所示:

    在这里插入图片描述
    除法运算过程如下:

    (1) 取被除数的高几位数据,位宽和除数相同(实例中是 3bit 数据)。
    (2) 将被除数高位数据与除数作比较,如果前者不小于后者,则可得到对应位的商为 1,两者做差得到第一步的余数;否则得到对应的商为 0,将前者直接作为余数。
    (3) 将上一步中的余数与被除数剩余最高位 1bit 数据拼接成新的数据,然后再和除数做比较。可以得到新的商和余数。
    (4) 重复过程 (3),直到被除数最低位数据也参与计算。
    需要说明的是,商的位宽应该与被除数保持一致,因为除数有可能为1。所以上述手动计算除法的实例中,第一步做比较时,应该取数字 27 最高位 1 (3’b001) 与 3’b101 做比较。 根据此计算过程,设计位宽可配置的流水线式除法器,流水延迟周期个数与被除数位宽一致。

    除法器设计
    单步运算设计

    单步除法计算时,单步被除数位宽(信号 dividend)需比原始除数(信号 divisor)位宽多 1bit 才不至于溢出。

    为了便于流水,输出端需要有寄存器来存储原始的除数(信号 divisor 和 divisor_kp)和被除数信息(信号 dividend_ci 和 dividend_kp)。

    单步的运算结果就是得到新的 1bit 商数据(信号 merchant)和余数(信号 remainder)。

    为了得到最后的除法结果,新的 1bit 商数据(信号 merchant)还需要与上一周期的商结果(merchant_ci)进行移位累加。

    单步运算单元设计如下(文件名 divider_cell.v):

    // parameter M means the actual width of divisor
    module    divider_cell
        #(parameter N=5,
          parameter M=3)
        (
          input                     clk,
          input                     rstn,
          input                     en,
    
          input [M:0]               dividend,
          input [M-1:0]             divisor,
          input [N-M:0]             merchant_ci , //上一级输出的商
          input [N-M-1:0]           dividend_ci , //原始除数
    
          output reg [N-M-1:0]      dividend_kp,  //原始被除数信息
          output reg [M-1:0]        divisor_kp,   //原始除数信息
          output reg                rdy ,
          output reg [N-M:0]        merchant ,  //运算单元输出商
          output reg [M-1:0]        remainder   //运算单元输出余数
        );
    
        always @(posedge clk or negedge rstn) begin
            if (!rstn) begin
                rdy            <= 'b0 ;
                merchant       <= 'b0 ;
                remainder      <= 'b0 ;
                divisor_kp     <= 'b0 ;
                dividend_kp    <= 'b0 ;
            end
            else if (en) begin
                rdy            <= 1'b1 ;
                divisor_kp     <= divisor ;  //原始除数保持不变
                dividend_kp    <= dividend_ci ;  //原始被除数传递
                if (dividend >= {1'b0, divisor}) begin
                    merchant    <= (merchant_ci<<1) + 1'b1 ; //商为1
                    remainder   <= dividend - {1'b0, divisor} ; //求余
                end
                else begin
                    merchant    <= merchant_ci<<1 ;  //商为0
                    remainder   <= dividend ;        //余数不变
                end
            end // if (en)
            else begin
                rdy            <= 'b0 ;
                merchant       <= 'b0 ;
                remainder      <= 'b0 ;
                divisor_kp     <= 'b0 ;
                dividend_kp    <= 'b0 ;
            end
        end 
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    流水级例化

    将单步计算的余数(信号 remainder)和原始被除数(信号 dividend)对应位的 1bit 数据重新拼接,作为新的单步被除数输入到下一级单步除法计算单元。

    其中,被除数、除数、及商的数据信息也要在下一级运算单元中传递。

    流水级模块例化完成除法的设计如下(文件名 divider_man.v):

    //parameter N means the actual width of dividend
    //using 29/5=5...4
    module    divider_man
        #(parameter N=5,
          parameter M=3,
          parameter N_ACT = M+N-1)
        (
          input                     clk,
          input                     rstn,
    
          input                     data_rdy ,  //数据使能
          input [N-1:0]             dividend,   //被除数
          input [M-1:0]             divisor,    //除数
    
          output                    res_rdy ,
          output [N_ACT-M:0]        merchant ,  //商位宽:N
          output [M-1:0]            remainder ); //最终余数
    
        wire [N_ACT-M-1:0]   dividend_t [N_ACT-M:0] ;
        wire [M-1:0]         divisor_t [N_ACT-M:0] ;
        wire [M-1:0]         remainder_t [N_ACT-M:0];
        wire [N_ACT-M:0]     rdy_t ;
        wire [N_ACT-M:0]     merchant_t [N_ACT-M:0] ;
    
        //初始化首个运算单元
        divider_cell      #(.N(N_ACT), .M(M))
           u_divider_step0
        ( .clk              (clk),
          .rstn             (rstn),
          .en               (data_rdy),
          //用被除数最高位 1bit 数据做第一次单步运算的被除数,高位补0
          .dividend         ({{(M){1'b0}}, dividend[N-1]}), 
          .divisor          (divisor),                  
          .merchant_ci      ({(N_ACT-M+1){1'b0}}),   //商初始为0
          .dividend_ci      (dividend[N_ACT-M-1:0]), //原始被除数
          //output
          .dividend_kp      (dividend_t[N_ACT-M]),   //原始被除数信息传递
          .divisor_kp       (divisor_t[N_ACT-M]),    //原始除数信息传递
          .rdy              (rdy_t[N_ACT-M]),
          .merchant         (merchant_t[N_ACT-M]),   //第一次商结果
          .remainder        (remainder_t[N_ACT-M])   //第一次余数
          );
    
        genvar               i ;
        generate
            for(i=1; i<=N_ACT-M; i=i+1) begin: sqrt_stepx
                divider_cell      #(.N(N_ACT), .M(M))
                  u_divider_step
                  (.clk              (clk),
                   .rstn             (rstn),
                   .en               (rdy_t[N_ACT-M-i+1]),
                   .dividend         ({remainder_t[N_ACT-M-i+1], dividend_t[N_ACT-M-i+1][N_ACT-M-i]}),   //余数与原始被除数单bit数据拼接
                   .divisor          (divisor_t[N_ACT-M-i+1]),
                   .merchant_ci      (merchant_t[N_ACT-M-i+1]), 
                   .dividend_ci      (dividend_t[N_ACT-M-i+1]), 
                   //output
                   .divisor_kp       (divisor_t[N_ACT-M-i]),
                   .dividend_kp      (dividend_t[N_ACT-M-i]),
                   .rdy              (rdy_t[N_ACT-M-i]),
                   .merchant         (merchant_t[N_ACT-M-i]),
                   .remainder        (remainder_t[N_ACT-M-i])
                  );
            end // block: sqrt_stepx
        endgenerate
    
        assign res_rdy       = rdy_t[0];
        assign merchant      = merchant_t[0];  //最后一次商结果作为最终的商
        assign remainder     = remainder_t[0]; //最后一次余数作为最终的余数
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    testbench

    取被除数位宽为 5,除数位宽为 3,testbench 中加入自校验,描述如下:

    `timescale 1ns/1ns
    
    module test ;
        parameter    N = 5 ;
        parameter    M = 3 ;
        reg          clk;
        reg          rstn ;
        reg          data_rdy ;
        reg [N-1:0]  dividend ;
        reg [M-1:0]  divisor ;
    
        wire         res_rdy ;
        wire [N-1:0] merchant ;
        wire [M-1:0] remainder ;
    
        //clock
        always begin
            clk = 0 ; #5 ;
            clk = 1 ; #5 ;
        end
    
        //driver
        initial begin
            rstn      = 1'b0 ;
            #8 ;
            rstn      = 1'b1 ;
    
            #55 ;
            @(negedge clk ) ;
            data_rdy  = 1'b1 ;
                    dividend  = 25;      divisor      = 5;
            #10 ;   dividend  = 16;      divisor      = 3;
            #10 ;   dividend  = 10;      divisor      = 4;
            #10 ;   dividend  = 15;      divisor      = 1;
            repeat(32)    #10   dividend   = dividend + 1 ;
            divisor      = 7;
            repeat(32)    #10   dividend   = dividend + 1 ;
            divisor      = 5;
            repeat(32)    #10   dividend   = dividend + 1 ;
            divisor      = 4;
            repeat(32)    #10   dividend   = dividend + 1 ;
            divisor      = 6;
            repeat(32)    #10   dividend   = dividend + 1 ;
        end
    
        //对输入延迟,便于数据结果同周期对比,完成自校验
        reg  [N-1:0]   dividend_ref [N-1:0];
        reg  [M-1:0]   divisor_ref [N-1:0];
        always @(posedge clk) begin
            dividend_ref[0] <= dividend ;
            divisor_ref[0]  <= divisor ;
        end
    
        genvar         i ;
        generate
            for(i=1; i<=N-1; i=i+1) begin
                always @(posedge clk) begin
                    dividend_ref[i] <= dividend_ref[i-1];
                    divisor_ref[i]  <= divisor_ref[i-1];
                end
            end
        endgenerate
    
        //自校验
        reg  error_flag ;
        always @(posedge clk) begin
        # 1 ;
            if (merchant * divisor_ref[N-1] + remainder != dividend_ref[N-1] && res_rdy) beginb      //testbench 中可直接用乘号而不考虑运算周期
                error_flag <= 1'b1 ;
            end
            else begin
                error_flag <= 1'b0 ;
            end
        end
    
        //module instantiation
        divider_man  #(.N(N), .M(M))
        u_divider
         (
          .clk              (clk),
          .rstn             (rstn),
          .data_rdy         (data_rdy),
          .dividend         (dividend),
          .divisor          (divisor),
          .res_rdy          (res_rdy),
          .merchant         (merchant),
          .remainder        (remainder));
    
       //simulation finish
       initial begin
          forever begin
             #100;
             if ($time >= 10000)  $finish ;
          end
       end
    
    endmodule // test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    仿真结果

    由图可知,2 个输入数据在延迟了和被除数相同位宽的周期数以后,输出了正确的除法结果。而且可流水式无延迟输出,符合设计。
    在这里插入图片描述

  • 相关阅读:
    Python将图片转换为ASCII字符画
    机器学习----奥卡姆剃刀定律
    flutter 开发中的问题与技巧
    生成模型(自编码器、VAE、GAN)
    我们下一代的 Linux 容器工具:Podman
    ELEVATE YOUR IAM POLICY GAME
    【JavaWeb】Cookie&Session
    多端开发之uniapp开发app
    PCL-MAL 聚己内酯马来酰亚胺
    自动驾驶感知算法实战16——激光雷达点云处理原理与实战
  • 原文地址:https://blog.csdn.net/qq_43158059/article/details/133653493