(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