Xilinx Divider Generator IP 实战:64位有符号除法、Fractional 输出解析与定点数转换

一、前言

在 FPGA 开发过程中,乘法、加法和减法相对容易理解,但除法运算通常比较复杂。

如果直接使用 Verilog 的 / 运算符,综合工具需要根据运算位宽和设计要求实现相应的除法电路,可能带来较大的逻辑资源开销和较长的组合路径。

对于一些对运算精度、时序和吞吐率有要求的 FPGA 工程,通常会考虑使用厂商提供的除法 IP。

Xilinx Vivado 提供了 Divider Generator IP,可以根据实际需求配置有符号/无符号运算、整数余数/小数输出、操作数位宽、流水线延迟等参数。

本文不打算逐项介绍 IP 的所有配置选项,而是结合一个实际工程,重点介绍以下内容:

  1. Divider Generator IP 的配置和基本使用。

  2. Signed + Fractional 模式下,IP 输出的数据格式。

  3. Quotient 和 Fractional 两部分如何组成一个完整的定点数。

  4. 为什么 Fractional 的符号位需要参与整数部分的减法。

  5. 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 的输入输出数据格式、符号位定义、流水线延迟以及上层定点数转换逻辑。

这些才是实际工程中真正需要掌握的内容。


参考资料

  1. AMD/Xilinx 官方文档:Divider Generator v5.1 LogiCORE IP Product Guide(PG151)

    AMD Technical Information Portal

  2. PG151 Chapter 2:Product Specification

    重点参考 Table 2-1,Radix-2 延迟计算公式。

  3. PG151 Chapter 3:Designing with the Core

    重点参考 Radix-2 Solution、Signed Fractional 输出说明、Non-Blocking Mode 和 TDATA Structure for Output。

  4. Vivado Divider Generator 5.1 IP 配置界面及 Implementation Details。

相关推荐
FPGA小迷弟6 小时前
FPGA 与千兆以太网校验和:从原理到 Verilog 实现
嵌入式硬件·fpga开发·无人机·ai编程·fpga
码农小韩1 天前
FPGA学习(二)——LED闪烁
fpga开发·verilog·fpga·led·组合逻辑·分频设计
FPGA小迷弟4 天前
FPGA 与千兆以太网:各类物理接口的形式、区别、使用方法与应用场景详解
tcp/ip·macos·fpga开发·udp·fpga
yingheceping5 天前
复旦微FPGA开发板厂商TOP5甄选榜:2026选型参考
fpga开发·fpga
这是谁的博客?7 天前
AI 加速器 RTL 数字系统设计深度解析:从 MAC 阵列 Verilog 实现到时序收敛与时钟功耗的芯片落地全链路
人工智能·macos·ai·verilog·数字ic设计·ai芯片·rtl
FPGA小迷弟8 天前
FPGA 与卫星:从星载硬件到在轨智能的完整技术解析
fpga开发·架构·fpga·1024程序员节·无线电
LabVIEW开发9 天前
FlexRIO 在轨图像取舍:判决要跑在数据进内存之前
labview·fpga·labview功能·labview程序·flexrio
FPGA小迷弟9 天前
FPGA 与无线电:从软件定义无线电到 5G 基带的硬件实现之路
5g·fpga开发·架构·dsp开发·fpga·无线电
yingheceping9 天前
2026 国产 FPGA 核心板怎么选?工控/视觉/通信/科研,四场景对板指南
fpga开发·fpga