1. Xilinx除法器IP核概述
在FPGA开发中,除法运算一直是个比较头疼的问题。硬件实现除法器不仅占用大量资源,时序还很难收敛。Xilinx提供的除法器IP核(Divider Generator)很好地解决了这个问题,它可以根据用户需求生成优化的除法器电路,支持多种算法和配置选项。
这个IP核特别适合需要高性能除法运算的场景,比如数字信号处理、通信系统、雷达信号处理等。我在最近的一个AD9361基带处理项目中就大量使用了这个IP核,实测下来性能稳定,资源占用合理。
2. IP核配置详解
2.1 基本参数配置
打开Vivado的IP Catalog,搜索"Divider Generator"就能找到这个IP核。配置界面主要分为几个部分:
Algorithm Type:算法类型,有三种选择:
- High Radix:高性能算法,适合高时钟频率
- LutMult:基于LUT的乘法器实现,适合小位宽
- Radix2:基于移位和减法,资源占用最少(我一般选这个)
Operand Sign:选择有符号(signed)还是无符号(unsigned)运算。这个选择会影响后续的输出格式处理。
位宽设置:
- Dividend Width:被除数位宽(我常用18位)
- Divisor Width:除数位宽(建议和被除数一致)
2.2 输出格式配置
Remainder Type:余数类型,两种模式:
- Remainder:输出余数(整数除法)
- Fractional:输出小数部分(更常用)
选择Fractional模式后,需要设置Fractional Width:小数部分位宽。这个值决定了输出精度,一般设为10-12位比较合适。
2.3 时序与控制配置
Clocks per Division:每完成一次除法运算需要的时钟周期数。值越小性能越高,但时序可能更难收敛。
Flow Control:流控模式,两种选择:
- Non Blocking:非阻塞模式,要求被除数和除数同时有效
- Blocking:阻塞模式,相当于在输入端口加了FIFO
Latency Configuration:延迟配置,一般选Automatic让工具自动优化。
提示:初次使用时建议先用默认的Radix2算法、Non Blocking模式和Automatic Latency,这些配置在大多数情况下都能工作得很好。
3. 工作模式与输出格式
3.1 非阻塞与阻塞模式时序
**非阻塞模式(Non Blocking)**的时序特点是:
- 需要dividend_tvalid和divisor_tvalid同时有效才会开始计算
- 输出结果会在固定延迟后出现在输出端口
- 适合能保证输入同步的场景
**阻塞模式(Blocking)**的时序特点是:
- 两个输入可以不同步,IP核内部会缓存
- 当两个输入都有效时才进行计算
- 适合输入可能不同步的场景
3.2 小数输出格式解析
当选择Fractional模式时,输出分为商(quotient)和小数(fractional)两部分。理解这个输出格式非常重要,特别是在有符号运算时。
无符号模式下:
小数输出 = 实际余数 × (2^F / 除数)
其中F是Fractional Width
有符号模式下:
小数输出 = 实际余数 × (2^(F-1) / 除数)
因为要保留1位符号位
3.3 有符号运算的7种输出情况
有符号除法运算的输出有7种可能的组合,每种情况的符号位处理都不同:
- 商=0,小数=0 → 符号位00
- 商=0,小数为正 → 符号位00
- 商=0,小数为负 → 符号位01
- 商为正,小数=0 → 符号位00
- 商为正,小数为正 → 符号位00
- 商为负,小数=0 → 符号位10
- 商为负,小数为负 → 符号位11
这7种情况在后续结果合并时需要区别处理,否则会得到错误的结果。
4. 结果合并的两种方法
在实际使用中,我们通常需要将商和小数部分合并成一个定点数。这里介绍两种我常用的方法:
4.1 拼接合并法
这种方法根据符号位的不同情况,采用不同的拼接规则:
verilog复制always @(posedge clk) begin
case({quotient[MSB], fractional[MSB]})
2'b00: result <= {quotient[MSB], quotient, fractional[MSB-1:0]};
2'b01: result <= {~quotient, fractional};
2'b10: result <= {quotient[MSB], quotient, fractional[MSB-1:0]};
2'b11: result <= {quotient[MSB], quotient-1'b1, fractional[MSB-1:0]};
endcase
end
4.2 相加合并法
这种方法将商左移小数位宽后与小数相加:
verilog复制always @(posedge clk) begin
result <= (quotient << FRAC_WIDTH) + fractional;
end
经验分享:相加法实现简单但可能溢出,拼接法更可靠但逻辑复杂。在时序紧张时建议用相加法,但要注意限制输入范围。
5. 仿真验证与实例分析
5.1 测试平台搭建
我通常会用类似下面的测试代码来验证除法器功能:
verilog复制`timescale 1ns / 1ps
module tb_divider;
reg clk = 0;
always #5 clk = ~clk;
// 测试用例
initial begin
// 情况1:商0,小数0
test_divide(0, 1536);
// 情况2:商0,小数正
test_divide(512, 1536);
// 情况3:商0,小数负
test_divide(-512, 1536);
// 其他情况...
end
task test_divide;
input [17:0] dividend;
input [17:0] divisor;
begin
@(posedge clk);
dividend_tvalid = 1;
divisor_tvalid = 1;
dividend_tdata = dividend;
divisor_tdata = divisor;
@(posedge clk);
dividend_tvalid = 0;
divisor_tvalid = 0;
repeat(20) @(posedge clk);
end
endtask
endmodule
5.2 典型测试用例分析
以-18.625/6.325 ≈ -2.9447这个计算为例:
-
首先将输入量化为定点数:
- -18.625 × 1024 = -19072
- 6.325 × 1024 = 6476
-
除法器输出:
- 商 = -2
- 小数 = -967
-
合并结果:
- 拼接法:{1'b1, 7'b1111101, 10'b0000111001} = -3015
- 3015/1024 ≈ 2.9443
-
误差分析:
- 理论值:-2.9447
- 实际结果:-2.9443
- 误差主要来自量化,增加小数位宽可以降低误差
5.3 实际应用技巧
-
位宽选择:被除数和除数位宽不宜过大,一般18-25位足够,否则会显著增加资源占用。
-
时序优化:如果时序紧张,可以增加Clocks per Division值,或者选择Blocking模式。
-
误差控制:小数位宽至少要10位才能保证1‰以内的精度,高精度应用建议12-14位。
-
异常处理:记得检查除数是否为0的情况,避免锁死整个系统。
6. 性能优化与资源占用
6.1 不同配置的资源对比
在我的Kintex-7器件上测试了几种配置:
| 配置 | LUT | FF | DSP | 最大频率(MHz) |
|---|---|---|---|---|
| Radix2, 18位 | 450 | 600 | 0 | 250 |
| HighRadix, 18位 | 800 | 900 | 2 | 350 |
| Radix2, 32位 | 1200 | 1500 | 0 | 180 |
6.2 优化建议
- 优先考虑Radix2算法,除非对性能要求极高
- 位宽能满足需求即可,不要过度设计
- 非阻塞模式比阻塞模式节省资源
- 适当增加latency可以提高时序性能
7. 常见问题排查
在实际使用中遇到过几个典型问题:
-
输出一直为0:
- 检查valid信号是否同时有效(非阻塞模式)
- 确认没有除0操作
- 检查IP核复位状态
-
结果不正确:
- 确认有符号/无符号配置正确
- 检查小数位宽设置
- 验证结果合并逻辑
-
时序违例:
- 增加Clocks per Division
- 选择Blocking模式
- 降低时钟频率
-
资源占用过高:
- 减小位宽
- 改用Radix2算法
- 考虑时分复用除法器
这个IP核在多个项目中都表现稳定,正确配置后基本不需要额外调试。最关键的是理解各种配置参数的含义,特别是小数输出格式的处理。建议第一次使用时先做充分的仿真验证,确保完全理解了IP核的行为后再集成到系统中。