除法器
关注我的公众号c137Lab获取更多相关内容
除法算法综述
线性收敛的除法算法
还原除法器&非执行除法器
最经典的顺序除法算法就是“手动计算”方法:
- 将被除数加载到余数寄存器中,除数和商放入各自寄存器,将除数寄存器的 LSB 对齐余数寄存器的 MSB
- 用余数寄存器减去除数寄存器,将结果保存在余数寄存器中
- 如果新的余数为正,将商寄存器的 LSB 设为 1 ,否则将商寄存器的 LSB 设置为0,并将被除数寄存器还原为做减法之前的状态
- 将商寄存器左移一位,除数寄存器右移一位,重复步骤 2,直到除数寄存器 LSB 对齐余数寄存器 LSB
由于步骤 3 中的还原计算,这种除法器也被称作还原除法器。还原除法器的代码实现如下:
module div_res(
input clk,rst_n,
input [7:0] Num,
input [5:0] Den,
output reg [5:0] Rem,
output reg [7:0] Quo
);
parameter s0=0, s1=1, s2=2, s3=3;
reg [3:0] count;
reg [1:0] state;
reg [13:0] Den_reg;
reg signed [13:0] Rem_reg;
reg [7:0] Quo_reg;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= s0;
else
case (state)
s0 : begin
state <= s1;
count <= 3'd0;
Quo_reg <= 7'd0;
Den_reg <= Den << 7;
Rem_reg <= Num;
end
s1 : begin
Rem_reg <= Rem_reg - Den_reg;
state <= s2;
end
s2 : begin
if (Rem_reg < 0) begin
Rem_reg <= Rem_reg + Den_reg;
Quo_reg <= Quo_reg << 1;
end else begin
Quo_reg <= (Quo_reg << 1) +1;
end
count <= count + 1;
Den_reg <= Den_reg >> 1;
if (count == 7) begin
state <= s3;
end else begin
state <= s1;
end
end
s3 : begin
Quo <= Quo_reg[7:0];
Rem <= Rem_reg[5:0];
state <= s0;
end
endcase
end
endmodule
不难发现其实这一方法可以通过很简单的方法减少步骤,改为非执行除法器。即在分母大于余数时,不执行减法。这样进行运算步骤数量会减少,但是会带来延迟路径的增加,并且可能会影响性能。非执行除法器最长路径有 if 和两个算数运算,而还原除法器只有 if 和一个算数运算。
非还原除法器
非还原除法器和非执行除法器相比没有增加关键路径,运行速度也更快,同时可以得到和还原除法器相近的性能。
非还原除法和还原除法相似,只是通过加商除数的操作免去了还原。步骤如下:
将被除数加载到余数寄存器中,除数和商放入各自寄存器,将除数寄存器的 LSB 对齐余数寄存器的 MSB
如果余数大于0,用余数寄存器减去除数寄存器,将结果保存在余数寄存器中,商寄存器 LSB 置1;如果余数小于0,用余数寄存器加上除数寄存器,将结果保存在余数寄存器中,商寄存器 LSB 置0
将商寄存器左移一位,除数寄存器右移一位,重复步骤 2,直到除数寄存器 LSB 对齐余数寄存器 LSB
最终的商等于商寄存器减去自己的反码,此时商为无符号数,余数为有符号数
如果想要商和余数都为相同符号,对于余数小于 0 时,通过
R = R + D , Q = Q − 1 R=R+D,Q=Q-1 R=R+D,Q=Q−1
实现校正
非还原除法器的代码如下(没有进行符号修正):
module div_nres(
input clk,rst_n,
input [7:0] Num,
input [5:0] Den,
output reg [5:0] Rem,
output reg [7:0] Quo
);
parameter s0=0, s1=1, s2=2;
reg [3:0] count;
reg [1:0] state;
reg [13:0] Den_reg;
reg signed [13:0] Rem_reg;
reg [7:0] Quo_reg;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= s0;
else
case (state)
s0 : begin
state <= s1;
count = 3'd0;
Quo_reg <= 7'd0;
Den_reg <= Den << 7;
Rem_reg <= Num;
end
s1 : begin
if (Rem_reg < 0) begin
Rem_reg <= Rem_reg + Den_reg;
Quo_reg <= Quo_reg << 1;
end else begin
Rem_reg <= Rem_reg - Den_reg;
Quo_reg <= (Quo_reg << 1) +1;
end
count = count + 1;
if (count == 8) begin
state <= s2;
end else begin
state <= s1;
Den_reg <= Den_reg >> 1;
end
end
s2 : begin
Quo <= Quo_reg[7:0] - ~ Quo_reg[7:0];
Rem <= Rem_reg[5:0];
state <= s0;
end
endcase
end
endmodule
快速除法器的设计
牛顿法
除法可以表示为倒数相乘的形式,即:
Q = N ∗ 1 D Q=N*\frac1D Q=N∗D1
即只需要想办法求出 D D D 的倒数即可,构造函数
f ( x ) = 1 x − D → 0 f(x)=\frac1x-D\rarr0 f(x)=x1−D→0
使用牛顿法进行迭代即
x k + 1 = x k − f ( x k ) f ′ ( x k ) x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)} xk+1=xk−f′(xk)f(xk)
代入 f ( x ) f(x) f(x) 得:
x k + 1 = x k ( 2 − D x k ) x_{k+1}=x_k(2-Dx_k) xk+1=xk(2−Dxk)
虽然牛顿法对于任意初值都会收敛,但是使用接近 1.0 1.0 1.0 的标准化值开始收敛更快(对于浮点尾数使用 0.5 ≤ D < 1 , o r 1 ≤ D < 2 0.5\leq D<1,or\ 1\leq D<2 0.5≤D<1,or 1≤D<2)
牛顿法每次迭代产生的误差为 e k = ( x k − 1 / D ) e_k=(x_k-1/D) ek=(xk−1/D) ,推导其误差公式:
e k + 1 = x k + 1 − x ∞ = x k ( 2 − D x k ) − 1 D e_{k+1}=x_{k+1}-x_\infty=x_k(2-Dx_k)-\frac1D ek+1=xk+1−x∞=xk(2−Dxk)−D1
= − D ( x k − 1 D ) 2 = − D e k 2 =-D(x_k-\frac1D)^2=-De_k^2 =−D(xk−D1)2=−Dek2
可以发现,误差在是二次收敛的,即每经过一次迭代,有效数字的位精度就增加一倍。
尽管牛顿法收敛速度很快,但是存在两个缺点:
- 迭代中的两次乘法是顺序进行的
- 乘法的顺序进行决定了量化误差的累积。为了避免量化误差通常需要使用额外的保护位
Anderson-Earle-Goldschmidt-Powers算法
这一算法改善了量化行为,并在每次迭代中并行计算两次乘法。在这一除法模式中,分子和分母都乘以近似因子 f k f_k fk ,对于足够的迭代次数,可以看到
D ∏ f k → 1 , N ∏ f k → Q D\prod f_k\rarr1,N\prod f_k\rarr Q D∏fk→1,N∏fk→Q
AEGP除法:
标准化 N , D N,D N,D 令 D D D 接近 1,利用标准化区间,如用于浮点数尾数的 0.5 ≤ D < 1 , o r 1 ≤ D < 2 0.5\leq D<1,or\ 1\leq D<2 0.5≤D<1,or 1≤D<2
初始化 x 0 = N , t 0 = D x_0=N,t_0=D x0=N,t0=D
重复以下循环直到 x k x_k xk 满足所需要的精度
f k = 2 − t k f_k=2-t_k fk=2−tk
x k + 1 = x k × f k x_{k+1}=x_k\times f_k xk+1=xk×fk
t k + 1 = t k × f k t_{k+1}=t_k\times f_k tk+1=tk×fk
由于除数和被除数同乘以同一因子 f k f_k fk ,因子中的任何量化误差都不重要。因此可以在前几次迭代中使用较少有效位的乘法器来降低所消耗的资源。
下面给出在 8 位精度下进行的9位定点数(1位整数位,8位分数位)除法代码:
module div_aegp(
input clk,rst_n,
input [8:0] Den,
input [8:0] Num,
output reg [8:0] Quo
);
parameter s0 = 0,s1 = 1, s2 = 2;
reg [1:0] state;
reg [9:0] x, t, f;
reg [3:0] count;
reg [17:0] tempx, tempt;
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 0)
state <= s0;
else
case (state)
s0 :begin
state <= 1;
count <= 0;
x <= {1'd0,Num};
t <= {1'd0,Den};
state <= s1;
end
s1 :begin
f = 512 - t;
tempx = x * f;
tempt = t * f;
count = count + 1;
x <= tempx >> 8;
t <= tempt >> 8;
if (count == 2)
state <= s2;
else
state <= s1;
end
s2 :begin
Quo <= x[8:0];
state <= s0;
end
endcase
end
endmodule
阵列除法器
和乘法/加法运算类似,也有阵列除法器,可以通过插入流水线提升频率,如果需要使用阵列除法器,直接调用 IP 是一个更好的选择。
参考文献:Digital Signal Processing with Field Programmable Gate Arrays --U.Meyer-Baese