FPGA在做信号处理相比cpu的优势对比

结合前文《FPGA 高速数字信号处理》的核心观点,我将分 6 大典型场景 ,用 C 语言(CPU/MCU 串行实现)Verilog(FPGA 硬件电路实现) 做逐行代码对比,覆盖:基础运算、FIR 滤波、乘法实现、流水线、数据延迟线、数据流架构。

每一组对比都会说明:执行逻辑、时钟周期、资源开销、性能差异 ,同时关联原文知识点、结合你当前电赛信号滤波 / 分离项目做落地解读。

前置约定

  1. CPU:通用处理器(ARM/PC/ 单片机),代码为标准 C 语言,串行指令执行
  2. FPGA:Xilinx Artix-7,Verilog 描述硬件电路,并行 / 流水线执行
  3. 时钟统一:系统时钟 100MHz(周期 10ns);
  4. 数据位宽:统一使用 16bit 有符号数(电赛信号处理通用 Q1.15 格式)。

场景 1:基础运算 y = a * x + b

原文对应知识点

CPU 是指令串行执行 ;FPGA 是直接搭建硬件通路,单周期完成运算

1.1 C 语言实现(CPU 串行)

c

运行

复制代码
// CPU/单片机 实现 y = a * x + b
#include <stdint.h>

// 16bit 有符号数据
int16_t calc(int16_t a, int16_t x, int16_t b)
{
    int16_t mul_res;
    int16_t y;

    // 步骤1:执行乘法(单独指令周期)
    mul_res = a * x;  
    // 步骤2:执行加法(单独指令周期)
    y = mul_res + b;  

    return y;
}
执行流程(CPU)
  1. 取指令 → 读取 a/x → 执行乘法 → 写回中间值(至少 2 个时钟周期);
  2. 读取中间值 + 读取 b → 执行加法 → 写回结果(额外 1 个时钟周期);
  3. 整体:至少 3 个时钟周期完成一次运算,存在指令调度、内存访问开销。

1.2 Verilog 实现(FPGA 硬件电路)

verilog

复制代码
// FPGA 实现 y = a * x + b,纯组合电路,单周期输出
module basic_calc(
    input  wire clk,
    input  wire signed [15:0] a,
    input  wire signed [15:0] x,
    input  wire signed [15:0] b,
    output reg signed [15:0]  y
);

always @(posedge clk) begin
    // 乘法、加法硬件直连,一周期完成
    y <= a * x + b; 
end

endmodule
硬件行为(FPGA)

综合后会生成:硬件乘法器 + 硬件加法器 直连电路。

  • 输入 a/x/b 同时进入电路;
  • 1 个时钟周期(10ns)直接输出结果
  • 无指令、无内存读写、无调度开销。

1.3 核心差异总结

表格

维度 CPU (C 语言) FPGA (Verilog)
执行方式 串行指令,分步执行 硬件电路并行,通路直连
单次运算耗时 ≥3 个时钟周期 1 个时钟周期
额外开销 指令取指、内存读写、调度 无额外开销
高速数据流适配 差,连续计算易拥堵 优,线速持续处理

场景 2:8 阶 FIR 滤波器(核心对比,原文重点案例)

FIR 公式: yn=h0​xn+h1​xn−1+h2​xn−2+...+h7​xn−7 h0​∼h7​ 为滤波器系数,xn−k 为历史采样数据。

原文对应知识点

CPU 用循环串行计算 ;FPGA 例化多组乘法器 + 加法树,全并行计算

2.1 C 语言实现(CPU 串行循环)

c

运行

复制代码
// 8阶 FIR 滤波器 - CPU 串行实现
#include <stdint.h>

// FIR 系数(16bit 定点)
const int16_t h[8] = {128, 256, 512, 1024, 1024, 512, 256, 128};
// 历史数据缓冲区(保存最近8个采样点)
int16_t x_buf[8] = {0};

int16_t fir_8order(int16_t x_in)
{
    int16_t y = 0;
    int i;

    // 1. 数据移位:更新历史缓冲区(串行移位)
    for(i = 7; i > 0; i--)
    {
        x_buf[i] = x_buf[i-1];
    }
    x_buf[0] = x_in;

    // 2. 串行循环:逐次乘加(循环8次)
    for(i = 0; i < 8; i++)
    {
        y += h[i] * x_buf[i]; 
    }

    return y;
}
执行流程(CPU)
  1. 数据移位:循环 7 次,7 个周期
  2. 乘累加:循环 8 次,每次 乘+加至少 16 个周期
  3. 整体:二十多个时钟周期输出 1 个滤波结果
  4. 新采样点必须等上一轮计算完成,吞吐率极低

2.2 Verilog 实现(FPGA 全并行 FIR)

verilog

复制代码
// 8阶 FIR 滤波器 - FPGA 并行架构
module fir_8order_parallel(
    input  wire clk,
    input  wire rst_n,
    input  wire signed [15:0] x_in,
    output reg signed [15:0]  y_out
);

// 1. 移位寄存器链:保存最近8个采样点 x[n]~x[n-7]
reg signed [15:0] x0, x1, x2, x3, x4, x5, x6, x7;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        {x0,x1,x2,x3,x4,x5,x6,x7} <= 0;
    end else begin
        x0 <= x_in;
        x1 <= x0;
        x2 <= x1;
        x3 <= x2;
        x4 <= x3;
        x5 <= x4;
        x6 <= x5;
        x7 <= x6;
    end
end

// 2. FIR 系数(16bit 定点)
localparam signed [15:0]
    h0 = 16'd128, h1 = 16'd256, h2 = 16'd512, h3 = 16'd1024,
    h4 = 16'd1024, h5 = 16'd512, h6 = 16'd256, h7 = 16'd128;

// 3. 并行乘法:8组乘法器同时工作(空间换时间)
wire signed [31:0] m0, m1, m2, m3, m4, m5, m6, m7;
assign m0 = x0 * h0;
assign m1 = x1 * h1;
assign m2 = x2 * h2;
assign m3 = x3 * h3;
assign m4 = x4 * h4;
assign m5 = x5 * h5;
assign m6 = x6 * h6;
assign m7 = x7 * h7;

// 4. 加法树:多级加法并行求和
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        y_out <= 0;
    else
        y_out <= m0 + m1 + m2 + m3 + m4 + m5 + m6 + m7;
end

endmodule
硬件行为(FPGA)
  1. 移位寄存器:1 个周期完成所有数据更新(并行打拍,不是循环移位);
  2. 8 组乘法器:同一时钟周期并行完成全部乘法
  3. 加法树:并行求和;
  4. 整体:每 1 个时钟周期输出 1 个滤波结果,满速吞吐。

2.3 核心差异总结

表格

维度 CPU (C 语言) FPGA (并行 Verilog)
计算方式 循环串行,逐次运算 多组硬件单元并行运算
单帧耗时 二十 + 时钟周期 1 个时钟周期
吞吐率 低,无法处理高速数据流 满速,100MHz 线速运行
资源开销 仅软件内存,无硬件成本 消耗多个乘法器、寄存器

结合你的电赛项目:你使用的 Vivado FIR IP 内部就是该并行 + 加法树架构,这也是 FPGA 能实时分离 50k/100kHz 信号的核心原因。


场景 3:乘法 / 乘累加实现:LUT 软乘法 VS DSP48E1 硬核

原文对应知识点

FPGA 优先使用 DSP48E1 专用硬核 做乘加运算,比 LUT 拼接的软乘法速度更快、资源更少、功耗更低。

3.1 方式 A:普通 * 运算符(LUT 拼接软乘法)

Verilog 中直接写 a*b,综合器会用 LUT+FF 拼接乘法器,不调用 DSP。

verilog

复制代码
// LUT 软乘法(不使用DSP硬核)
module mul_lut(
    input  wire clk,
    input  wire signed [15:0] a,
    input  wire signed [15:0] b,
    output reg signed [31:0]  res
);

always @(posedge clk) begin
    res <= a * b; // 综合为 LUT 组合逻辑乘法
end

endmodule
缺陷
  • 速度上限:约 100MHz;
  • 资源:占用数百个 LUT/FF;
  • 时序差,高阶滤波极易违规。

3.2 方式 B:直接调用 DSP48E1 原语(官方推荐硬核)

对应原文给出的 DSP 乘累加 A*B+C 代码,FPGA 算力核弹。

verilog

复制代码
// 调用 Xilinx DSP48E1 硬核:实现 MAC = A*B + C
module mul_dsp48e1(
    input  wire clk,
    input  wire rst,
    input  wire [24:0] A,
    input  wire [17:0] B,
    input  wire [47:0] C,
    output wire [47:0] P
);

// DSP48E1 原语配置:OPMODE = 0001101 → A*B + C
DSP48E1 #(
    .A_INPUT("DIRECT"),
    .B_INPUT("DIRECT"),
    .OPMODE(6'b0001101) 
) dsp_mac_inst (
    .CLK(clk),
    .RST(rst),
    .A(A),
    .B(B),
    .C(C),
    .P(P),
    .CEA1(1'b1), .CEA2(1'b1),
    .CEB1(1'b1), .CEB2(1'b1),
    .CEC(1'b1), .CEP(1'b1)
);

endmodule
优势
  • 速度上限:600MHz+;
  • 资源:仅占用 1 个 DSP Slice,几乎不消耗 LUT;
  • 专为滤波、FFT 乘加运算优化,电赛高速 DSP 必用。

3.3 核心差异总结

表格

维度 LUT 软乘法 DSP48E1 硬核乘法
最高主频 ~100MHz 600MHz+
逻辑资源 (LUT/FF) 大量占用 几乎不占用
适用场景 低速简易运算 FIR/FFT/DDS 高速乘加(主力)

电赛建议:手写滤波 / 乘加代码时,优先调用 DSP 原语 ,不要依赖 * 运算符。


场景 4:流水线架构:无流水线 VS 多级流水线

原文对应知识点

流水线切割长组合逻辑,提升主频、拉高持续吞吐;仅增加单数据延迟,不影响连续数据流。

y = (in * coeff) + bias 三级运算为例对比。

4.1 无流水线(长组合逻辑)

所有运算在同一级组合逻辑完成,关键路径长,主频上不去

verilog

复制代码
// 无流水线:单级组合逻辑
module no_pipeline(
    input  wire clk,
    input  wire signed [15:0] in,
    input  wire signed [15:0] coeff,
    input  wire signed [15:0] bias,
    output reg signed [15:0]  out
);

always @(posedge clk) begin
    // 乘法+加法 全部在一级逻辑内完成
    out <= in * coeff + bias; 
end

endmodule
问题

组合逻辑路径过长,综合后最高主频被限制(Artix-7 通常 <150MHz)。


4.2 三级流水线(寄存器切割路径)

插入寄存器分为三级:输入锁存 → 乘法 → 加法输出。

verilog

复制代码
// 三级流水线架构(原文标准示例)
module pipeline_3stage(
    input  wire clk,
    input  wire signed [15:0] in,
    input  wire signed [15:0] coeff,
    input  wire signed [15:0] bias,
    output reg signed [15:0]  out
);

// 流水线寄存器
reg signed [15:0] stage1_reg;  // 第1级:输入锁存
reg signed [31:0] stage2_reg;  // 第2级:乘法结果

always @(posedge clk) begin
    stage1_reg  <= in;                  // 工位1:采集数据
    stage2_reg  <= stage1_reg * coeff;  // 工位2:乘法运算
    out         <= stage2_reg + bias;    // 工位3:加法输出
end

endmodule
硬件行为
  1. 第 1 拍:数据进入 stage1;
  2. 第 2 拍:数据进入 stage2;
  3. 第 3 拍:第一个有效结果输出;
  4. 从第 3 拍开始,每一拍持续输出新结果
  5. 组合逻辑被拆分,主频可轻松跑到 250MHz+

4.3 核心差异总结

表格

维度 无流水线 三级流水线
单数据延迟 (Latency) 1 个周期 3 个周期(延迟增加)
持续吞吐率 一般,主频受限 极高,满速线速运行
最高运行主频 低 (<150MHz) 高 (>250MHz)

结合你的项目:FIR/FFT IP 默认开启流水线,是滤波能实时处理高频信号的关键。


场景 5:滤波器延迟线:寄存器移位链 VS BRAM 环形缓冲区

原文对应知识点

高阶滤波禁止使用超长寄存器链,BRAM 环形缓冲区 是工程标准方案,节省资源、布线简单。

以 1024 阶 FIR 延迟线为例。

5.1 方式 A:寄存器数组移位(低效,不推荐)

verilog

复制代码
// 错误写法:1024阶延迟线 - 纯寄存器移位链
reg [15:0] delay_line [1023:0]; // 1024个寄存器

always @(posedge clk) begin
    // 循环移位:消耗大量FF + 布线爆炸
    for(int i = 1023; i > 0; i = i-1) begin
        delay_line[i] <= delay_line[i-1];
    end
    delay_line[0] <= x_in;
end
缺陷
  1. 占用 1024+ 触发器,资源爆满;
  2. 大规模寄存器链布线复杂,时序必违规;
  3. 阶数越高,问题越严重。

5.2 方式 B:BRAM 环形缓冲区(工程标准写法)

利用 FPGA 片上 BRAM 做循环缓存,几乎不消耗逻辑资源

verilog

复制代码
// 高效写法:BRAM 环形缓冲区(1024点延迟线)
module bram_delay_line(
    input  wire clk,
    input  wire rst_n,
    input  wire [15:0] din,
    output wire [15:0] dout
);

localparam DEPTH = 1024;
reg [9:0] wr_ptr;  // 写指针(循环地址)

// 1. 循环写指针(0~1023 循环)
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_ptr <= 0;
    else
        wr_ptr <= (wr_ptr == DEPTH-1) ? 10'd0 : wr_ptr + 1'd1;
end

// 2. 例化 单端口BRAM(Vivado BRAM IP)
bram_1024x16 u_bram(
    .clka(clk),
    .wea(1'b1),    // 始终使能写
    .addra(wr_ptr),
    .dina(din),
    .addrb(wr_ptr - 10'd7), // 读取历史抽头数据
    .doutb(dout)
);

endmodule
优势
  1. 仅占用 1 块 BRAM,FF/LUT 几乎无消耗;
  2. 地址指针循环读写,支持任意高阶滤波器;
  3. 布线简单,时序稳定。

5.3 核心差异总结

表格

维度 寄存器移位链 BRAM 环形缓冲区
资源占用 大量 FF 仅 1 块 BRAM
布线 / 时序 差,高阶必违规 优,时序稳定
可拓展阶数 低(<100 阶) 极高(数千阶)

结合你的项目:DDS 波形表、高阶 FIR、图像行缓存,全部基于 BRAM 实现。


场景 6:跨时钟域数据流(异步 FIFO 对比)

原文对应知识点

不同时钟域(如 ADC/125MHz 网口时钟 + 100MHz 系统时钟)必须用 异步 FIFO 隔离,防止亚稳态。

6.1 错误写法:直接跨时钟域连线(亚稳态根源)

verilog

复制代码
// 错误:两个异步时钟直接传数据,产生亚稳态
wire [15:0] adc_data;  // ADC时钟:125MHz
reg [15:0] sys_data;  // 系统时钟:100MHz

always @(posedge sys_clk) begin
    sys_data <= adc_data; // 异步时钟直连 → 毛刺、功能不稳定
end

6.2 正确写法:异步 FIFO 隔离时钟域(工程标准)

verilog

复制代码
// 正确:异步FIFO 隔离 125MHz(ADC) ↔ 100MHz(系统)
async_fifo u_fifo(
    .wr_clk(adc_clk),  // 写时钟:ADC 125MHz
    .rd_clk(sys_clk),  // 读时钟:系统 100MHz
    .din(adc_data),
    .dout(sys_data),
    .wr_en(1'b1),
    .rd_en(1'b1)
);

结合你的项目:如果后续使用「外部模拟加法器 + ADC 采集」方案,必须加异步 FIFO


全局总结 & 电赛落地规则

一、CPU vs FPGA 核心代码思维差异

  1. C 语言 (CPU) :描述执行步骤,串行循环、指令驱动,适合低速、复杂业务逻辑;
  2. Verilog(FPGA) :描述硬件电路 ,并行、流水线、存储分级,适合高速实时信号处理

二、电赛 FPGA 信号处理代码编写准则(直接套用)

  1. 乘加运算:优先调用 DSP48E1 原语 ,少用普通 * 运算符;
  2. 高阶延迟线:用 BRAM 环形缓冲区,禁止超长寄存器移位链;
  3. 高速数据流:必须加 流水线寄存器,提升主频与吞吐;
  4. 跨时钟域:大批量数据用 异步 FIFO,单路信号用两级打拍同步;
  5. 通用算法(FIR/FFT/DDS):优先使用 Vivado 官方 IP,不手写复杂算法;
  6. 定点运算:全程管控位宽,关键节点加饱和截位,防止波形溢出失真。

三、选型速记

  • 低速、简单逻辑 → 普通组合逻辑 + 寄存器;
  • 高速乘加、滤波 → DSP48E1 + 流水线;
  • 大数据缓存、长延迟 → BRAM;
  • 跨时钟域数据流 → 异步 FIFO。
相关推荐
Szime1 小时前
AD9218国产替代方向:双通道10位105MSPS ADC深智微科技选型经验
fpga开发
江鸟的坚持1 小时前
xilinx xadc 例化
fpga开发·xadc·xilinx xadc
明德扬2 小时前
AD采集卡适配方案交流:模块、板卡与FPGA示例工程支持
fpga开发
尤老师FPGA3 小时前
HDMI数据的接收发送实验(十四)
fpga开发
Szime13 小时前
全球首创10位40GSPS超宽带ADC选型参考:国产超高速ADC深智微科技选型支持
科技·单片机·嵌入式硬件·fpga开发
Szime18 小时前
AD9653、AD9253、AD9694国产替代怎么评估?深智微科技整理ADI高速ADC选型思路
科技·fpga开发
FPGA小徐18 小时前
Xilinx zynq-7000系列FPGA移植Linux操作系统详细教程
fpga开发·架构
Zebros21 小时前
LC无线无源传感器读取方案设计研究综述
fpga开发·信息与通信·射频工程
国科安芯1 天前
商业航天通信载荷数字处理单元供电架构研究——基于ASP7A84AS的高精度低压差线性稳压器技术分析
前端·单片机·嵌入式硬件·fpga开发·架构·安全性测试