一、前言
在 FPGA 开发过程中,乘法、加法和减法相对容易理解,但除法运算通常比较复杂。
如果直接使用 Verilog 的 / 运算符,综合工具需要根据运算位宽和设计要求实现相应的除法电路,可能带来较大的逻辑资源开销和较长的组合路径。
对于一些对运算精度、时序和吞吐率有要求的 FPGA 工程,通常会考虑使用厂商提供的除法 IP。
Xilinx Vivado 提供了 Divider Generator IP,可以根据实际需求配置有符号/无符号运算、整数余数/小数输出、操作数位宽、流水线延迟等参数。
本文不打算逐项介绍 IP 的所有配置选项,而是结合一个实际工程,重点介绍以下内容:
-
Divider Generator IP 的配置和基本使用。
-
Signed + Fractional 模式下,IP 输出的数据格式。
-
Quotient 和 Fractional 两部分如何组成一个完整的定点数。
-
为什么 Fractional 的符号位需要参与整数部分的减法。
-
IP 的流水线延迟、有效信号和工程中的数据对齐。
本文使用的 IP 版本为 Divider Generator 5.1,相关定义以 AMD/Xilinx 官方文档 PG151 为准。
二、Divider Generator IP 配置
2.1 IP 配置界面
本工程使用的 Divider Generator IP 配置如下。

主要配置参数:
| 配置项 | 参数值 | 说明 |
|---|---|---|
| Algorithm Type | Radix2 | Radix-2 非恢复余数算法 |
| Operand Sign | Signed | 有符号二进制补码 |
| Dividend Width | 64 | 被除数 64 bit |
| Divisor Width | 64 | 除数 64 bit |
| Remainder Type | Fractional | 输出小数部分 |
| Fractional Width | 64 | 小数输出 64 bit |
| Detect Divide-By-Zero | Enable | 检测除数为 0 |
这里重点关注三个参数:
1. Operand Sign = Signed
表示输入的被除数和除数均按照有符号二进制补码进行解释。
例如:
64'h0000_0000_0000_0005 = 5
64'hFFFF_FFFF_FFFF_FFFB = -5
需要注意,硬件接口上的 TDATA 本质上仍然是 64 bit 数据总线,Signed 配置决定 IP 如何解释这些二进制数据。
2. Remainder Type = Fractional
这里选择 Fractional,而不是普通的 Remainder。
两者的区别:
-
Remainder:输出整数余数。
-
Fractional:输出小数部分,相当于将商继续计算到二进制小数点之后。
本文重点讨论 Fractional 模式。
3. Fractional Width = 64
表示小数部分输出 64 bit。
对于 Signed 模式,这 64 bit 并不是普通的无符号小数,而是一个具有符号位的二进制补码定点数,后面会重点分析。
2.2 Options 配置

Options 配置如下:
| 配置项 | 参数值 | 说明 |
|---|---|---|
| Clocks per Division | 1 | 每个时钟周期可以启动一次除法 |
| Flow Control | Non Blocking | 非阻塞模式 |
| Optimize Goal | Performance | 优先考虑性能 |
| Latency Configuration | Automatic | 自动配置延迟 |
| Latency | 132 | 132 个时钟周期 |
| ACLKEN | Disabled | 不启用时钟使能 |
| ARESETN | Disabled | 不启用 IP 复位端口 |
其中,Latency = 132 是一个比较重要的参数。
它决定输入数据进入 IP 后,需要经过多少个时钟周期才能得到有效输出。
这里先记住 132,后面会结合 PG151 的延迟公式进行推导。
另外,本配置启用了 Divide-By-Zero 检测,因此输出端会提供一个 TUSER 信号,用于指示是否发生除零。
三、Divider Generator 的输入输出数据格式
3.1 输入数据
当前工程中,Divider Generator 的输入包括:
reg signed [63:0] div_den;
reg signed [63:0] div_real_num;
reg signed [63:0] div_imag_num;
其中:
-
div_den:除数。 -
div_real_num:实部被除数。 -
div_imag_num:虚部被除数。
工程中分别例化了两个 Divider Generator:
div_gen_0 u_f1_real_div (
.aclk (i_clk),
.s_axis_divisor_tvalid (div_in_valid),
.s_axis_divisor_tdata (div_den),
.s_axis_dividend_tvalid(div_in_valid),
.s_axis_dividend_tdata (div_real_num),
.m_axis_dout_tvalid (real_div_valid),
.m_axis_dout_tuser (real_div_zero),
.m_axis_dout_tdata (real_div_out)
);
div_gen_0 u_f1_imag_div (
.aclk (i_clk),
.s_axis_divisor_tvalid (div_in_valid),
.s_axis_divisor_tdata (div_den),
.s_axis_dividend_tvalid(div_in_valid),
.s_axis_dividend_tdata (div_imag_num),
.m_axis_dout_tvalid (imag_div_valid),
.m_axis_dout_tuser (imag_div_zero),
.m_axis_dout_tdata (imag_div_out)
);
可以看到,两个 IP 使用相同的除数,分别计算实部和虚部的除法结果。
这里需要注意:
Divider Generator 本身并不知道上层设计使用的是 Q 格式还是其他定点数格式。
它只按照配置,将输入的 64 bit 数据解释为有符号整数,然后执行除法,并按照 Fractional Width 输出小数部分。
因此,输入和输出的定点数精度管理,需要由上层 RTL 自己完成。
3.2 输出数据结构
这是使用 Divider Generator 时非常重要的一点。
根据 Vivado 的 Implementation Details,本工程输出数据结构为:
M_AXIS_DOUT_TDATA[127:0]
+--------------------------+--------------------------+
| QUOTIENT | FRACTIONAL |
+--------------------------+--------------------------+
| [127:64] | [63:0] |
+--------------------------+--------------------------+
| int64 | fix64_63 |
+--------------------------+--------------------------+
也就是说:
| 字段 | 位宽 | 数据类型 |
|---|---|---|
| QUOTIENT | 64 bit | int64 |
| FRACTIONAL | 64 bit | fix64_63 |
其中:
-
QUOTIENT:64 bit 有符号整数部分。
-
FRACTIONAL:64 bit 有符号 Q1.63 小数部分。
注意:这里的 fix64_63 表示总位宽为 64 bit,其中有 63 bit 小数位,最高位为符号位。
因此,Fractional 字段实际上也是一个独立的有符号二进制补码数。
在 RTL 中,对输出进行拆分:
wire [127:0] real_div_out;
reg signed [63:0] parse_real_quot;
reg signed [63:0] parse_real_frac;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
parse_real_quot <= 64'd0;
parse_real_frac <= 64'd0;
end else if (div_pair_valid) begin
parse_real_quot <= real_div_out[127:64];
parse_real_frac <= real_div_out[63:0];
end
end
这里一定要记住:
QUOTIENT 和 FRACTIONAL 是两个独立的有符号数据字段,不能简单地将它们当成一个普通的 128 bit 有符号整数。
后续需要对它们进行格式转换。
四、Signed + Fractional 模式下,Fractional 究竟如何表示?
这一部分是理解后续 RTL 的关键。
4.1 先看 PG151 给出的官方例子
AMD 官方文档 PG151 在 Signed Fractional 模式下给出了一个例子:
$$ [
-9/4=-2.25
] $$
对应的输出:
| 输出字段 | 二进制 | 有符号数值 |
|---|---|---|
| QUOTIENT | 11110 | -2 |
| FRACTIONAL | 11100 | -0.25 |
这里假设 Fractional Width 为 5 bit,仅用于方便理解。
其中:
QUOTIENT = 11110 = -2
FRACTIONAL = 11100 = -4
Fractional 有 5 bit,其中 1 bit 为符号位,剩余 4 bit 为小数位。
因此:
$$ [
FRACTIONAL=-4/2^4=-0.25
] $$
最终:
$$ [
-2+(-0.25)=-2.25
] $$
这说明:
Signed + Fractional 模式下,QUOTIENT 和 FRACTIONAL 都有自己的符号位。
这也是 PG151 明确说明的行为。

4.2 64 bit Fractional 的位权关系
回到本工程:
reg signed [63:0] parse_real_frac;
它是一个 Q1.63 格式的有符号小数。
按照二进制补码表示,各位权重如下:
| Bit | 权重 |
|---|---|
| bit63 | -1 |
| bit62 | +2^-1 |
| bit61 | +2^-2 |
| bit60 | +2^-3 |
| ... | ... |
| bit0 | +2^-63 |
所以,Fractional 的实际数值可以表示为:
$$ [
F=-F63+\frac{U(F62:0)}{2^{63}}
] $$
其中:
-
F63:小数部分的符号位。
-
U(F62:0):将低 63 bit 按无符号数解释。
这个公式非常重要。
因为它说明:
Fractional 的最高位并不只是一个符号标志,它本身具有 -1 的数值权重。
举个例子:
假设:
$$ [
F=-0.25
] $$
对应的 64 bit 二进制补码为:
64'hE000_0000_0000_0000
此时:
F[63] = 1
F[62:0] = 0.75
按照 Q1.63 的位权:
$$ [
F=-1+0.75=-0.25
] $$
注意这里:
去掉最高位以后,剩下的 63 bit 表示的是 +0.75,而不是 -0.25。
这就是后面为什么需要进行整数修正的原因。
五、为什么需要执行 Quotient - Fractional63?
这是本文最重要的内容,也是实际工程中最容易看不懂的地方。
5.1 先看直接拼接会有什么问题
假设除法结果为:
$$ [
-9/4=-2.25
] $$
Divider Generator 输出:
QUOTIENT = -2
FRACTIONAL = -0.25
根据前面的分析:
FRACTIONAL[63] = 1
FRACTIONAL[62:0] = 0.75
如果直接将 Quotient 和 Fractional 的低 63 bit 拼接:
assign real_q63 = {
parse_real_quot[63],
parse_real_quot,
parse_real_frac[62:0]
};
那么拼接后的实际数值为:
$$ [
-2+0.75=-1.25
] $$
而我们希望得到的是:
$$ [
-2.25
] $$
显然,直接拼接会导致结果错误。
问题在哪里?
原来的 Fractional 是 -0.25,但去掉最高位以后,低 63 bit 已经变成了 +0.75。
因此,必须将丢弃最高位所对应的 -1 权重转移到整数部分。
也就是:
$$ [
-2-1+0.75=-2.25
] $$
所以,正确的整数部分应该是:
$$ [
Q_{\text{new}}=Q-F63
] $$
最终:
$$ [
\boxed{
Result=(Q-F63)+\frac{U(F62:0)}{2^{63}}
}
] $$
这就是需要执行减法的根本原因。
5.2 再看一个更容易理解的例子
考虑:
$$ [
-1/4=-0.25
] $$
此时:
QUOTIENT = 0
FRACTIONAL = -0.25
也就是说:
QUOTIENT[63] = 0
FRACTIONAL[63] = 1
可以发现:
Quotient 的符号位为 0,但 Fractional 的符号位为 1。
如果直接使用 Quotient 的符号位,并丢弃 Fractional 的符号位:
整数部分 = 0
小数部分 = 0.75
最终结果就会变成:
$$ [
+0.75
] $$
而不是:
$$ [
-0.25
] $$
正确的处理方式:
Quotient = 0
Fractional[63] = 1
修正后的整数部分 = 0 - 1 = -1
小数部分 = 0.75
因此:
$$ [
-1+0.75=-0.25
] $$
这个例子可以直接说明:
Quotient 的符号位只能表示整数部分的符号,不能代替 Fractional 的符号位。
5.3 对应到工程 RTL
回到实际代码:
assign real_hi_a = $signed({
parse_real_quot[DIV_W-1],
parse_real_quot
});
assign real_hi_b = $signed({
{DIV_W{1'b0}},
parse_real_frac[DIV_W-1]
});
其中:
real_hi_a
是将 64 bit 有符号 Quotient 扩展到 65 bit。
对应:
$$ [
real_hi_a=Q
] $$
而:
real_hi_b
是将 Fractional 的符号位扩展为 65 bit。
由于高 64 bit 全部补 0,因此:
parse_real_frac[63] = 0
real_hi_b = 0
或者:
parse_real_frac[63] = 1
real_hi_b = 1
对应:
$$ [
real_hi_b=F63
] $$
接下来通过 wide_addsub_pipe 执行减法:
wide_addsub_pipe #(
.DATA_W (DIV_W+1)
) u_real_q63_hi (
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_calc_valid (parse_valid),
.i_calc_a (real_hi_a),
.i_calc_b (real_hi_b),
.i_calc_sub (1'b1),
.o_calc_valid (real_hi_valid),
.o_calc (real_q63_hi)
);
这里 i_calc_sub = 1'b1 表示执行减法。
所以:
$$ [
\boxed{real_q63_hi=Q-F63}
] $$
为了更直观地理解 Fractional63 的作用,下面以 -9/4 = -2.25 为例,将 Fractional 的符号位转移到整数部分的过程进行可视化展示。
从图中可以看到,直接丢弃 Fractional63 会导致结果从 -2.25 变成 -1.25。只有先执行 Quotient - Fractional[63],再与 Fractional62:0 拼接,才能保证最终数值不变。

实部和虚部采用相同的处理方式。
5.4 为什么这里使用 65 bit?
注意代码中的定义:
wire signed [DIV_W:0] real_hi_a;
wire signed [DIV_W:0] real_hi_b;
wire signed [DIV_W:0] real_q63_hi;
这里不是 64 bit,而是 65 bit。
原因是:
原始 Quotient 是 64 bit 有符号整数,现在需要执行:
$$ [
Q-1
] $$
为了避免减法结果在 64 bit 有符号运算中的边界溢出,先将 Quotient 符号扩展到 65 bit,再执行减法。
这样可以保留更大的中间运算范围。
六、最终如何组成统一的 128 bit 定点数?
前面已经得到了:
real_q63_hi = parse_real_quot - parse_real_frac[63];
那么低 63 bit 怎么处理?
很简单,直接取 Fractional 的低 63 bit:
parse_real_frac[62:0]
但由于前面的减法采用了流水线结构,所以需要对低 63 bit 以及其他关联信息进行相同的延迟处理。
工程中对应代码:
assign parse_meta_in = {
parse_q_idx,
parse_alias_real,
parse_alias_imag,
parse_real_zero,
parse_imag_zero,
parse_real_frac[DIV_W-2:0],
parse_imag_frac[DIV_W-2:0]
};
通过 signal_delay 对这些数据进行延迟。
之后取出对齐的小数部分:
assign hi_real_frac =
parse_meta_out[2*DIV_FRAC_BITS-1:DIV_FRAC_BITS];
最后:
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
real_q63 <= {D_DIV_W{1'b0}};
end else if (hi_pair_valid) begin
real_q63 <= {real_q63_hi, hi_real_frac};
end
end
最终得到:
real_q63 = {
real_q63_hi,
hi_real_frac
};
对应的数学表达式:
$$ [
\boxed{
real_q63=
{Q-F63,F62:0}
}
] $$
最终数据结构:
real_q63[127:0]
+----------------------------+---------------------------+
| 整数部分 | 小数部分 |
+----------------------------+---------------------------+
| [127:63] | [62:0] |
+----------------------------+---------------------------+
| signed 65 bit | unsigned 63 bit |
+----------------------------+---------------------------+
这样就得到了一个 128 bit 的有符号定点数,其中:
-
高 65 bit:有符号整数部分。
-
低 63 bit:无符号二进制小数部分。
注意:这里并不是简单地将两个有符号数拼接,而是先将 Fractional 的符号位所代表的 -1 权重转移到整数部分,然后再进行拼接。
七、从 128 bit 定点数转换为最终的 Q2.30
完成上述处理后,工程还需要将 128 bit 的高精度结果转换为最终使用的 32 bit 系数。
相关参数:
localparam DIV_W = 64;
localparam DIV_FRAC_BITS = DIV_W - 1;
localparam FRAC_W = 30;
localparam DIV_OUT_SHIFT_BITS = DIV_FRAC_BITS - FRAC_W;
代入:
$$ [
DIV_FRAC_BITS=64-1=63
] $$
$$ [
DIV_OUT_SHIFT_BITS=63-30=33
] $$
因此需要将 128 bit 定点数右移 33 bit,并进行舍入处理。
对应代码:
shift_round #(
.I_DATA_WID (D_DIV_W),
.O_DATA_WID (COEF_W),
.SHIFT_BITS (DIV_OUT_SHIFT_BITS)
) u_f1_real_round (
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_round_valid(q63_valid),
.i_round_dat (real_q63),
.o_round_valid(f1_real_round_valid),
.o_round_dat (f1_real_round)
);
最终输出:
reg signed [31:0] f1_real;
也就是一个 32 bit 有符号定点数,其中 30 bit 为小数位,通常称为 Q2.30(这里采用包含符号位的整数位计数方式)。
整个转换过程可以总结为:
Divider Generator 输出
|
v
+-----------------------+
| QUOTIENT 64 bit |
| FRACTIONAL 64 bit |
+-----------------------+
|
v
整数部分修正:
Q - FRACTIONAL[63]
|
v
低63位直接保留:
FRACTIONAL[62:0]
|
v
+-----------------------+
| 128 bit signed fixed |
| 63 bit fractional |
+-----------------------+
|
v
右移33位
|
v
舍入处理
|
v
+-----------------------+
| 32 bit signed Q2.30 |
+-----------------------+
到这里,Divider Generator 的输出格式转换就完成了。
八、Divider Generator 的流水线延迟和数据对齐
除法 IP 不仅要关注数据格式,还必须关注流水线时序。
这也是实际工程中非常容易出问题的地方。
8.1 为什么延迟是 132 个时钟周期?
根据 AMD 官方 PG151,Radix-2 算法在 Fully Pipelined 配置下,Signed + Fractional 模式且 Clocks per Division = 1 时,延迟公式为:
$$ [
Latency=M+F+A+4
] $$
其中:
-
M:Dividend / Quotient Width。
-
F:Fractional Width。
-
A:AXI 接口增加的延迟。
本工程配置:
M = 64
F = 64
A = 0(NonBlocking 模式)
代入:
$$ [
Latency=64+64+0+4
] $$
$$ [
\boxed{Latency=132}
] $$
因此,Vivado 自动生成的 Latency = 132 是符合 PG151 公式的。
8.2 Latency 和 Throughput 的区别
这里需要特别注意两个概念:
Latency(延迟):
一组输入数据从进入 IP 到对应输出有效,需要经过多少个时钟周期。
本工程为 132 个时钟周期。
Throughput(吞吐率):
单位时间内可以处理多少组数据。
由于本工程设置:
Clocks per Division = 1
因此采用全流水线结构,流水线填满之后,理论上每个时钟周期都可以输出一组除法结果。
也就是说:
Latency = 132 cycles
Throughput = 1 division / cycle
不要将 132 个时钟周期的延迟误认为每隔 132 个时钟周期才能进行一次除法。
8.3 NonBlocking 模式下的输入有效信号
当前工程使用:
Flow Control = Non Blocking
在这种模式下,需要注意:
Divider Generator 有两个输入通道:
-
s_axis_dividend
-
s_axis_divisor
一次除法操作需要两个输入通道同时提供有效数据。
如果只有一个通道的 TVALID 有效,另一个通道无效,那么这次操作不会发生。
因此,当前工程将两个输入通道的 TVALID 连接到同一个有效信号:
.s_axis_divisor_tvalid (div_in_valid),
.s_axis_dividend_tvalid (div_in_valid)
这样可以保证两个输入通道在同一个时钟周期启动一次除法运算。
另外,NonBlocking 模式不支持输出端的 TREADY 背压控制。如果下游无法持续接收数据,需要额外设计 FIFO 或选择适合的 Blocking 模式。
8.4 为什么需要对 metadata 进行延迟?
除法运算通常不只有数据本身,还会有一些关联信息。
例如本工程中的:
div_in_q_idx
div_in_alias_real
div_in_alias_imag
这些数据需要和除法结果保持一一对应。
由于 Divider Generator 延迟为 132 个时钟周期,因此工程中使用:
signal_delay #(
.DATA_WIDTH (DIV_META_W),
.DELAY_BEATS (DIV_IP_DELAY)
) u_div_meta_delay (
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_sig_valid (div_in_valid),
.i_sig_data (div_meta_in),
.o_sig_valid (div_meta_valid),
.o_sig_data (div_meta_out)
);
这里:
localparam DIV_IP_DELAY = 132;
就是为了让 metadata 和 Divider Generator 输出的数据对齐。
最终:
assign div_pair_valid =
real_div_valid &
imag_div_valid &
div_meta_valid;
只有实部除法、虚部除法以及 metadata 三者都有效时,才认为当前结果有效。
后续的 wide_addsub_pipe 还会对整数部分进行流水线减法,因此低 63 bit 小数部分也需要按照 ADD_DELAY 进行延迟。
这里有一个工程原则:
任何经过流水线运算的数据,其对应的 valid 和 metadata 都必须按照相同的实际延迟进行对齐。
否则,即使数值运算本身正确,也可能出现数据和索引、标志位错位的问题。
需要注意,signal_delay 的延迟参数应与实际寄存器级数定义一致,最终应通过仿真波形确认输入、输出 valid 的边沿对应关系。
九、实际工程中的几个注意事项
9.1 除数为 0 的处理
本工程启用了:
Detect Divide-By-Zero = Enable
因此 IP 输出端会提供:
.m_axis_dout_tuser(real_div_zero)
根据 PG151,除数为 0 时,商和小数结果均没有定义,不能直接使用输出数值。
工程中还做了一层分母保护:
assign den_le_one = ~(|den_q64[DIV_W-1:1]);
当分母是非负数时,该表达式检测其数值是否为 0 或 1。
随后:
if (den_le_one) begin
div_den <= DEN_EPS;
end else begin
div_den <= den_q64;
end
其中:
localparam DEN_EPS = 64'd1;
即将分母至少钳位到 1 LSB,避免将 0 直接送入除法 IP。
不过,IP 的 Divide-By-Zero 标志仍然需要保留,作为额外的异常检测手段。
另外,TUSER 标志应当与对应的 m_axis_dout_tvalid 一起使用。
9.2 IP 的复位问题
当前配置截图中:
ARESETN = Disabled
因此生成的 Divider Generator 没有 ARESETN 输入端口。
虽然外围 RTL 使用:
always @(posedge i_clk or negedge i_rst_n)
对 valid 和其他寄存器进行复位,但这并不意味着 Divider Generator 内部流水线也被 i_rst_n 复位。
如果需要复位 IP 内部状态,需要在 IP 配置中启用 ARESETN。
根据 PG151,ARESETN 是同步低有效复位,至少需要保持两个 aclk 周期。
因此,工程设计时要区分:
-
外围 RTL 的复位。
-
IP 内部的复位。
-
数据是否有效由 TVALID 判断。
不要仅仅因为外围寄存器复位了,就认为 IP 内部流水线也已经清空。
9.3 Radix2 和 High Radix 如何选择?
Divider Generator 提供多种除法实现算法。
其中:
| 算法 | 特点 |
|---|---|
| Radix-2 | 基于 FPGA 逻辑资源实现,适合高吞吐率场景,不使用 DSP 和 BRAM |
| High Radix | 使用预缩放和高基数算法,利用 DSP、BRAM,适合较宽操作数 |
PG151 对算法选择给出的建议是:
-
Radix-2:较小位宽、高吞吐率或希望节省 DSP 的场景。
-
High Radix:较大位宽场景,通常从约 16 bit 以上开始值得考虑。
本工程虽然采用 64 bit 操作数,但选择 Radix-2,主要需要结合实际的吞吐率、资源和时序要求进行评估。
不能简单认为位宽越大就一定应该使用 High Radix,最终还是要通过 Vivado 综合和实现结果进行比较。
9.4 有符号运算需要注意边界溢出
对于 64 bit 有符号数,其表示范围为:
$$ [
-2^{63}\sim 2^{63}-1
] $$
其中存在一个特殊情况:
$$ [
-2^{63}/(-1)=2^{63}
] $$
结果无法用 64 bit 有符号数表示。
PG151 也特别提醒了这个问题。如果应用场景可能出现最小负数除以 -1,需要考虑扩展 Quotient 位宽。
本工程的分母来自平方和,正常情况下为非负数,因此该特殊组合不属于预期运算场景,但设计其他通用除法模块时需要注意。
十、总结
本文结合一个实际 FPGA 工程,介绍了 Xilinx Divider Generator IP 的配置、输入输出格式以及有符号 Fractional 的处理方式。
最后总结几个需要记住的知识点:
1. Signed + Fractional 模式下,Quotient 和 Fractional 都是独立的有符号数。
不能简单认为 Quotient 的符号位就代表整个除法结果的符号。
2. 64 bit Fractional 实际上是 Q1.63 有符号小数。
其最高位的权重为 -1,而不是普通无符号小数的 +1。
3. Fractional 的符号位参与整数部分减法,是为了完成正确的定点数格式转换。
数学关系为:
$$ [
\boxed{
Result=(Q-F63)+\frac{U(F62:0)}{2^{63}}
}
] $$
因此,RTL 中的:
real_q63_hi = real_hi_a - real_hi_b;
并不是额外修改除法结果,而是将有符号 Fractional 转换为可以直接拼接的无符号小数部分。
4. IP 的 Latency 和 Throughput 是两个不同概念。
本工程的延迟为 132 个时钟周期,但流水线填满后可以实现每个时钟周期输出一组结果。
5. 流水线设计必须重视 valid 和 metadata 对齐。
不能只关注数据计算正确,还需要保证数据、索引和标志位在整个流水线中始终对应同一笔运算。
对于 FPGA 初学者来说,使用厂商 IP 时,不要只停留在会配置、会例化的层面。更重要的是理解 IP 的输入输出数据格式、符号位定义、流水线延迟以及上层定点数转换逻辑。
这些才是实际工程中真正需要掌握的内容。
参考资料
-
AMD/Xilinx 官方文档:Divider Generator v5.1 LogiCORE IP Product Guide(PG151)
-
PG151 Chapter 2:Product Specification
重点参考 Table 2-1,Radix-2 延迟计算公式。
-
PG151 Chapter 3:Designing with the Core
重点参考 Radix-2 Solution、Signed Fractional 输出说明、Non-Blocking Mode 和 TDATA Structure for Output。
-
Vivado Divider Generator 5.1 IP 配置界面及 Implementation Details。