(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) beginif (reset) beginstate <= IDLE;busy <= 1'b0;valid_o <= 1'b0;quotient <= {OW{1'b0}};remainder <= {DW{1'b0}};count <= 6'd0;endelse begincase (state)IDLE: beginif (valid_i && !busy) beginif (divisor == 0) begin // 除数为0检查state <= DONE;quotient <= {OW{1'b1}}; // 设置为最大值表示错误remainder <= {DW{1'b1}};valid_o <= 1'b1;endelse beginstate <= 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); // 初始移位endendendCALC: beginif (count > 0) beginif (dividend_reg >= shifted_divisor) begindividend_reg <= dividend_reg - shifted_divisor;quotient_temp[count-1] <= 1'b1;endelse beginquotient_temp[count-1] <= 1'b0;endshifted_divisor <= shifted_divisor >> 1;count <= count - 1;endelse beginstate <= DONE;endendDONE: beginquotient <= quotient_temp;remainder <= dividend_reg[DW-1:0];valid_o <= 1'b1;busy <= 1'b0;state <= IDLE;enddefault: beginstate <= IDLE;endendcaseend
endendmodule