Bootstrap

数字图像处理(6):除法运算、除法器

(1)当除数是常数时,可以先转化为乘法,再右移,乘法的N越大,计算误差越小。

        如:计算x/122,可以看成(x * 67)>>13,N=13,使用verilog实现:

reg     [15:0]  x;
reg     [9:0]   y;
//y= x /122
assign y = ((x << 6) + (x << 1) + x) >> 13;

(2)使用状态机实现一个除法器

预算规则:

  • 将除数扩大到和被除数同位宽,比较其大小。
  • 如果被除数更大,则上位1;反之,上位0。如果被除数更大,临时除数要等于被除数减掉对应扩大后的除数,反之不用。
  • 继续前两步骤,直至被除数同临时被除数同位宽。
module divide #(
    parameter IW = 32,      // 被除数位宽
    parameter DW = 9,       // 除数位宽
    parameter OW = IW-DW    // 商的位宽
)(
    input  wire             clk,
    input  wire             reset,
    input  wire             valid_i,
    input  wire [IW-1:0]    dividend,    // 被除数
    input  wire [DW-1:0]    divisor,     // 除数
    output reg              valid_o,      // 输出有效信号
    output reg  [OW-1:0]    quotient,    // 商
    output reg  [DW-1:0]    remainder    // 余数
);

// 内部信号定义
reg [IW-1:0]    dividend_reg;    // 被除数寄存器
reg [DW-1:0]    divisor_reg;     // 除数寄存器
reg [OW-1:0]    quotient_temp;   // 临时商
reg [IW-1:0]    diff;            // 差值
reg [5:0]       count;           // 计数器
reg             busy;            // 除法器忙状态
reg [IW-1:0]    shifted_divisor; // 移位后的除数

// 状态定义
localparam IDLE = 3'b001;
localparam CALC = 3'b010;
localparam DONE = 3'b100;
reg [2:0] state;

// 状态机和除法逻辑
always @(posedge clk or posedge reset) begin
    if (reset) begin
        state <= IDLE;
        busy <= 1'b0;
        valid_o <= 1'b0;
        quotient <= {OW{1'b0}};
        remainder <= {DW{1'b0}};
        count <= 6'd0;
    end
    else begin
        case (state)
            IDLE: begin
                if (valid_i && !busy) begin
                    if (divisor == 0) begin  // 除数为0检查
                        state <= DONE;
                        quotient <= {OW{1'b1}};  // 设置为最大值表示错误
                        remainder <= {DW{1'b1}};
                        valid_o <= 1'b1;
                    end
                    else begin
                        state <= CALC;
                        busy <= 1'b1;
                        valid_o <= 1'b0;
                        dividend_reg <= dividend;
                        divisor_reg <= divisor;
                        quotient_temp <= {OW{1'b0}};
                        count <= OW;
                        shifted_divisor <= divisor << (OW-1);  // 初始移位
                    end
                end
            end

            CALC: begin
                if (count > 0) begin
                    if (dividend_reg >= shifted_divisor) begin
                        dividend_reg <= dividend_reg - shifted_divisor;
                        quotient_temp[count-1] <= 1'b1;
                    end
                    else begin
                        quotient_temp[count-1] <= 1'b0;
                    end
                    
                    shifted_divisor <= shifted_divisor >> 1;
                    count <= count - 1;
                end
                else begin
                    state <= DONE;
                end
            end

            DONE: begin
                quotient <= quotient_temp;
                remainder <= dividend_reg[DW-1:0];
                valid_o <= 1'b1;
                busy <= 1'b0;
                state <= IDLE;
            end

            default: begin
                state <= IDLE;
            end
        endcase
    end
end

endmodule

;