Verilog实现32位除法器

  实现乘法器有多种思路,本次作业采用的是不恢复余数的方式。下面是对两种参考思路的介绍。

基于移位、减法的恢复余数除法器

  对于32位无符号除法,可将被除数a转换成高32位为0低32位是a的数temp_a,在每个周期开始时 temp_a 向左移动一位,最后一位补零,然后判断 temp_a 的高 32 位是否大于等于除数b,如是则 temp_a 的高32位减去b并且加1,得到的值赋给 temp_a,如果不是则直接进入下一步,执行结束后 temp_a 的高 32 位即为余数,低 32 位即为商。对于 32 位有符号除法,可先将有符号数转换成无符号数除法,根据被除数和除数的符号判断商的符号,被除数是负数时余数为负,否则为正。

不恢复余数除法器

  不恢复余数即不管相减结果是正还是负,都把它写入 reg_r,若为负,下次迭代不是从中减去除数而是加上除数。

Verilog实现

  无符号除法器时钟信号下降沿时检查 start 信号,有效时开始执行,执行除法指令时,busy 标志位置 1。在执行除法指令时,任何情况下不产生算数异常,当除数为 0 时,运算结果未知,对除法器除数为 0 和溢出情况的发生通过汇编指令中其他指令进行检查和处理。无符号运算 Verilog 代码如下:

`timescale 1ns / 1ps

module DIVU(
    input   [31:0]  dividend,   //被除数
    input   [31:0]  divisor,    //除数
    input           start,      //启动除法运算
    input           clock,
    input           reset,
    output  [31:0]  q,          //商
    output  [31:0]  r,          //余数
    output reg      busy        //除法器忙标志位
);
    reg     [4:0]   count;
    reg     [31:0]  reg_q;
    reg     [31:0]  reg_r;
    reg     [31:0]  reg_b;
    reg             r_sign;
    wire    [32:0]  sub_add = r_sign? ({reg_r,q[31]} + {1'b0,reg_b}):({reg_r,q[31]} - {1'b0,reg_b}); //加、减法器
    assign r = r_sign? reg_r + reg_b : reg_r;
    assign q = reg_q;
    
    always @(negedge clock or posedge reset)
    begin
        if (reset == 1) 
        begin
            count   <=  5'b0;
            busy    <=  0;
        end 
        else 
        begin
            if (start&&(!busy)) 
            begin //初始化
                reg_r   <= 32'b0;
                r_sign  <= 0;
                reg_q   <= dividend;
                reg_b   <= divisor;
                count   <= 5'b0;
                busy    <= 1'b1;
            end 
            else if (busy) 
            begin //循环操作
                reg_r   <= sub_add[31:0];   //部分余数
                r_sign  <= sub_add[32];
                reg_q   <= {reg_q[30:0],~sub_add[32]};
                count   <= count + 5'b1;    //计数器加一
                if (count == 5'b11111)  busy <= 0;
            end
        end
    end
endmodule

  有符号运算中基本和无符号除法器类似,需要注意余数应与被除数符号一致,Verilog 代码如下:

`timescale 1ns / 1ps

module DIV(
    input   [31:0]  dividend,   //被除数
    input   [31:0]  divisor,    //除数
    input           start,      //启动除法运算
    input           clock,
    input           reset,
    output  [31:0]  q,          //商
    output  [31:0]  r,          //余数
    output reg      busy        //除法器忙标志位
);
    reg     [4:0]   count;
    reg     [31:0]  reg_q;
    reg     [31:0]  reg_r;
    reg     [31:0]  reg_b;
    reg             q_sign;
    reg             r_sign;
    reg             a_sign;
    wire    [32:0]  sub_add = r_sign? {reg_r,reg_q[31]}+{1'b0,reg_b}:{reg_r,reg_q[31]}-{1'b0,reg_b};
    assign r = a_sign? (-(r_sign? reg_r+reg_b:reg_r)):(r_sign? reg_r+reg_b:reg_r);
    assign q = q_sign? -reg_q:reg_q;
    
    always@(negedge clock or posedge reset)
    begin
        if(reset)
            begin count<=0;busy<=0;end
        else if(start&&(!busy))
        begin 
            count<=0;reg_q<=dividend[31]?-dividend:dividend;
            reg_r<=0;
            reg_b<=divisor[31]?-divisor:divisor;
            r_sign<=0;busy<=1'b1;
            q_sign<=dividend[31]^divisor[31];
            a_sign<=dividend[31];
        end
        else if(busy)
        begin
            reg_r<=sub_add[31:0];
            r_sign<=sub_add[32];
            reg_q<={reg_q[30:0],~sub_add[32]};
            count<=count+1;
            if(count==31)   busy<=0;
        end
    end
endmodule