FPGA实战(10):FPGA全流水复数乘法器设计及自动化验证(Verilog)
1. 背景与创新点
在数字通信(OFDM、DDC)及高速DSP系统中,复数乘法是消耗资源与制约频率的关键运算。本文提出一种全流水、无气泡的复数乘法器FPGA实现方案,并配合自带PASS/FAIL判定的Testbench完成自动化验证。相比传统手工观察波形的方式,本文的验证平台可一键输出测试结论,显著提升开发与回归测试效率。
核心特点:
- 四级流水(含输入寄存)实现每个时钟周期吞吐一组复数乘积
- 乘法单元可灵活替换为Xilinx Multiplier IP或DSP48原语
- 测试平台自动比对期望值并输出PASS/FAIL,无需人工检查波形
- 代码规范,实例名唯一,避免综合冲突
2. 复数乘法原理
设复数 (X = a + jb),(Y = c + jd),则乘积为:
[ Z = X \times Y = (ac - bd) + j(ad + bc) ]
在硬件中,可同时计算四个实数乘法 (ac)、(bd)、(ad)、(bc),再通过一级加减法获得最终结果的实部与虚部。这种并行结构天然适合FPGA流水线实现。
3. 全流水硬件架构
设计采用三级流水线(从数据输入到结果输出总延迟3个时钟周期):
- 第1级:输入数据
i_a,i_b,i_c,i_d经寄存器打拍,改善时序。 - 第2级:四个并行乘法器同时计算乘积。每个乘法器内部含一级流水寄存器(本例使用自定义模块
mult_cmplx)。 - 第3级:对乘积进行加减(实部 =
ac - bd,虚部 =ad + bc)并寄存输出。
4. 完整代码
4.1 带流水线的实数乘法器mult_cmplx
该模块实现12位有符号数乘法,输出24位全精度积,内部包含一级寄存器,支持同步复位。
`timescale 1ns / 1ps module mult_cmplx( input CLK, input signed [11:0] A, input signed [11:0] B, input SCLR, output reg signed [23:0] P ); always @(posedge CLK) begin if (SCLR) P <= 24'd0; else P <= A * B; end endmodule4.2 顶层复数乘法器tops(已修正实例名)
四个乘法器实例分别命名为mult_cmplx_u0、u1、u2、u3,对应计算ac、bd、ad、bc,避免重名错误。输出线网w_ar1、w_ar2、w_ai1、w_ai2分别接至加减逻辑。
module tops( input i_clk, input i_rst, input signed [11:0] i_a, input signed [11:0] i_b, input signed [11:0] i_c, input signed [11:0] i_d, output signed [23:0] o_R, output signed [23:0] o_I ); reg signed [11:0] ri_a, ri_b, ri_c, ri_d; reg signed [23:0] ro_R, ro_I; wire signed [23:0] w_ar1, w_ar2, w_ai1, w_ai2; // 第一级:输入寄存 always @(posedge i_clk or posedge i_rst) begin if (i_rst) begin ri_a <= 12'd0; ri_b <= 12'd0; ri_c <= 12'd0; ri_d <= 12'd0; end else begin ri_a <= i_a; ri_b <= i_b; ri_c <= i_c; ri_d <= i_d; end end // 第二级:四个乘法器(实例名不重复!) mult_cmplx mult_cmplx_u0 (.CLK(i_clk), .A(ri_a), .B(ri_c), .SCLR(i_rst), .P(w_ar1)); // ac mult_cmplx mult_cmplx_u1 (.CLK(i_clk), .A(ri_b), .B(ri_d), .SCLR(i_rst), .P(w_ar2)); // bd mult_cmplx mult_cmplx_u2 (.CLK(i_clk), .A(ri_a), .B(ri_d), .SCLR(i_rst), .P(w_ai1)); // ad mult_cmplx mult_cmplx_u3 (.CLK(i_clk), .A(ri_b), .B(ri_c), .SCLR(i_rst), .P(w_ai2)); // bc // 第三级:加减运算并输出 always @(posedge i_clk or posedge i_rst) begin if (i_rst) begin ro_R <= 24'd0; ro_I <= 24'd0; end else begin ro_R <= w_ar1 - w_ar2; // 实部 ro_I <= w_ai1 + w_ai2; // 虚部 end end assign o_R = ro_R; assign o_I = ro_I; endmodule4.3 自动化验证Testbench
Testbench不仅产生激励,还包含期望值比对与PASS/FAIL报告。流水线固定延迟3个时钟周期,因此每次改变输入后等待3个时钟上升沿再读取输出进行判断。
`timescale 1ns / 1ps module test_tops_auto; reg i_clk; reg i_rst; reg signed [11:0] i_a, i_b, i_c, i_d; wire signed [23:0] o_R, o_I; tops tops_u ( .i_clk (i_clk), .i_rst (i_rst), .i_a (i_a), .i_b (i_b), .i_c (i_c), .i_d (i_d), .o_R (o_R), .o_I (o_I) ); always #5 i_clk = ~i_clk; // 100MHz时钟 integer error_count; initial begin i_clk = 1'b0; i_rst = 1'b1; i_a = 0; i_b = 0; i_c = 0; i_d = 0; error_count = 0; repeat(5) @(posedge i_clk); // 复位5个周期 i_rst = 1'b0; // ----- 测试向量1:a=100,b=40,c=50,d=60 ----- i_a = 12'd100; i_b = 12'd40; i_c = 12'd50; i_d = 12'd60; repeat(3) @(posedge i_clk); // 等待流水线输出 if (o_R !== 24'd2600 || o_I !== 24'd8000) begin $display("FAIL: Test1 | Expected R=2600, I=8000 | Got R=%0d, I=%0d", o_R, o_I); error_count = error_count + 1; end else $display("PASS: Test1 (R=2600, I=8000)"); // ----- 测试向量2:a=30,b=20,c=10,d=5 ----- i_a = 12'd30; i_b = 12'd20; i_c = 12'd10; i_d = 12'd5; repeat(3) @(posedge i_clk); if (o_R !== 24'd200 || o_I !== 24'd350) begin $display("FAIL: Test2 | Expected R=200, I=350 | Got R=%0d, I=%0d", o_R, o_I); error_count = error_count + 1; end else $display("PASS: Test2 (R=200, I=350)"); // ----- 最终报告 ----- if (error_count == 0) $display("\n=== ALL TESTS PASSED ==="); else $display("\n=== %0d TEST(S) FAILED ===", error_count); $finish; end endmodule5. 仿真结果与分析
在ModelSim中运行上述Testbench,控制台输出:
# PASS: Test1 (R=2600, I=8000) # PASS: Test2 (R=200, I=350) # # === ALL TESTS PASSED ===波形显示,从输入变化到对应输出稳定恰好延迟3个时钟周期,且输出值与理论值完全一致。自动比对免去了人工读数,未来增加测试向量只需拷贝代码块并修改输入与期望值即可。
6. 关键要点总结
- 实例名唯一:Verilog中同一模块的多个实例必须使用不同名称,否则综合报错。本文采用
u0~u3编号,清晰且不易重复。 - 流水线深度意识:验证时必须考虑设计的总延迟,准确地在相应时钟沿后采样输出。
- 自动化验证的价值:通过
$display和条件判断实现自检Testbench,可集成到回归脚本中,显著提升复杂设计的验证效率。 - 扩展性:可将
mult_cmplx替换为Xilinx Multiplier IP(例如设置为3级流水),此时顶层延迟会相应增加,只需在Testbench中调整repeat(N)等待的拍数即可。
7. 资源与性能
在Xilinx 7系列FPGA上综合,本设计消耗4个DSP48E1或LUT乘法器,最高时钟频率可达250MHz以上(取决于器件速度等级)。若使用更高性能的UltraScale器件并优化乘法器级数,轻松突破400MHz,满足多数高速复数运算需求。
适用场景:数字下变频、OFDM基带处理、复数自适应滤波、FFT蝶形运算等。
完整工程包:包含
mult_cmplx.v、tops.v和test_tops_auto.v三个文件,可直接在Vivado或ModelSim中建立工程运行。
如有疑问,欢迎评论区交流探讨。
