FPGA教程系列-Vivado中串行FIR设计(非FIR核)

FPGA教程系列-Vivado中串行FIR设计(非FIR核)

什么是FIR滤波器

Finite Impulse Response Filter, 有限冲激响应滤波器,是数字信号处理里最常用、最基础、也最稳定 的一类滤波器。"只有零点,没有极点 "的滤波器:输出只依赖有限个过去和当前的输入样本 ,不依赖以前的输出,因此系统总是稳定的。FIR 滤波器是有限长单位冲击响应滤波器。直接型结构如下:

FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积,表达式如下:

冲激响应(滤波器系数)

h[n] 是FIR滤波器的冲激响应 ,即滤波器对单位冲激信号的响应序列。h[n]直接对应系数b_n (如h[0]=b₀, h[1]=b₁, ...),用于加权输入信号及其延迟值

通俗理解:h是滤波器的"权重",决定了滤波器的特性(如低通、高通等)。

延迟算子

z⁻¹ 是Z变换中的延迟算子 ,表示信号延迟一个采样周期。通俗理解:z⁻¹是"记忆单元",用于存储输入信号的过去值,供后续加权计算。

Matlab仿真滤波器

FIR滤波器有好几种形式,简单的用matlab程序,仿真出一组系数:

matlab 复制代码
clc;
clear;
close all;

% 滤波器参数
order = 10;       % 滤波器阶数 (系数个数 = order + 1)
cutoff = 0.4;     % 截止频率 (归一化)

% 1. 使用汉明窗设计
h_hamming = fir1(order, cutoff, 'low', hamming(order+1));

% 2. 使用汉宁窗设计
h_hanning = fir1(order, cutoff, 'low', hanning(order+1));

% 3. 使用布莱克曼窗设计 (旁瓣衰减更好,但过渡带更宽)
h_blackman = fir1(order, cutoff, 'low', blackman(order+1));

% 绘制频率响应进行比较
figure;
freqz(h_hamming, 1, 1024);
hold on;
freqz(h_hanning, 1, 1024);
freqz(h_blackman, 1, 1024);
title('不同窗函数设计的低通滤波器频率响应');
legend('Hamming', 'Hanning', 'Blackman');
grid on;

%量化参数
h_x = round(1023*h_hanning) %

运行以后可以生成一组参数:

这个就是可以在vivado中使用的参数。

Vivado新建工程

有了参数以后,就可以在vivado中建立工程,用单纯的Verilog来编写程序的话,会有很多重复的部分,而System Verilog可以实现参数化的操作,因此这部分选用System Verilog来进行编写。

建立Top文件,fir_top.sv:

verilog 复制代码
`timescale 1ns / 1ps

module fir_top #(
    // --- 参数定义 ---
    parameter TAPS = 11,                     // 滤波器阶数
    parameter DATA_IN_W = 2,                 // 输入数据位宽
    parameter COEF_W = 14,                   // 系数位宽 (与原IP核匹配)
    parameter DATA_OUT_W = 20                // 输出数据位宽 (16+ceil(log2(11))=20)
)(
    input  i_clk,
    input  i_rst,
    input  signed [DATA_IN_W-1:0] i_din,
    output signed [DATA_OUT_W-1:0] o_dout
);

    // --- 11阶滤波器系数定义 ---
    // 0, -19, -32, 71, 288, 407, 288, 71, -32, -19, 0
    localparam signed [COEF_W-1:0] COEF [0:TAPS-1] = '{
        14'd0,    14'sd19, 14'sd32, 14'd71, 14'd288, 14'd407,
        14'd288,  14'd71,  14'sd32, 14'sd19, 14'd0
    };
    // 注意:Verilog中负数需要用'sd声明,或者在位宽前加s,如14'sd19

    // --- 延迟链 (移位寄存器) ---
    reg signed [DATA_IN_W-1:0] x_reg [0:TAPS-1];
    integer i;
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            for (i = 0; i < TAPS; i = i + 1) begin
                x_reg[i] <= {DATA_IN_W{1'b0}};
            end
        end else begin
            x_reg[0] <= i_din;
            for (i = 1; i < TAPS; i = i + 1) begin
                x_reg[i] <= x_reg[i-1];
            end
        end
    end

    // --- 乘法器输出线网数组 ---
    wire signed [15:0] r [0:TAPS-1]; // 假设乘法器IP核输出为16位

    // --- 使用 generate 循环实例化乘法器 ---
    genvar g;
    generate
        for (g = 0; g < TAPS; g = g + 1) begin : mul_gen
            multer multer_inst (
                .CLK(i_clk),
                .A(x_reg[g]),      // 连接到对应的延迟寄存器
                .B(COEF[g]),       // 连接到对应的系数
                .SCLR(i_rst),
                .P(r[g])           // 输出到对应的线网
            );
        end
    endgenerate

    // --- 求和 ---
    assign o_dout = r[0] + r[1] + r[2] + r[3] + r[4] + r[5] + r[6] + r[7] + r[8] + r[9] + r[10];

endmodule

乘法器采用的是vivado的IP核,可以参考以前的设计,进行设计。注意是带复位信号的乘法器。

top写完以后可以编写testbench文件:

verilog 复制代码
`timescale 1ns / 1ps

module test_fir;
reg i_clk;
reg i_rst;
reg signed[1:0]i_din;
wire signed[15:0]o_dout;

fir_tops fir_tops_u(
    .i_clk              (i_clk),
    .i_rst              (i_rst),
    .i_din              (i_din),
    .o_dout             (o_dout)
);

// 时钟生成:周期10ns,频率100MHz
always #5 i_clk = ~i_clk;

initial
begin
    // 初始化
    i_clk = 1'b1;
    i_rst = 1'b1;
    i_din = 2'b00;
    
    // 复位持续100ns
    #100 i_rst = 1'b0;
    
    // ==================== 测试序列设计 ====================
    
    // 1. 阶跃响应测试 (0 -> +1)
    $display("=== 阶跃响应测试开始 ===");
    #50 i_din = 2'b01;  // 输入+1,观察滤波器如何从0上升到稳态值
    #200;               // 持续200ns,观察完整的上升过程
    
    // 2. 负阶跃响应测试 (+1 -> -1)
    $display("=== 负阶跃响应测试开始 ===");
    i_din = 2'b11;      // 输入-1,观察滤波器的负向响应
    #200;               // 持续200ns
    
    // 3. 回零测试 (-1 -> 0)
    $display("=== 回零测试开始 ===");
    i_din = 2'b00;      // 输入0,观察滤波器回到零点
    #200;               // 持续200ns
    
    // 4. 单脉冲测试 (0 -> +1 -> 0)
    $display("=== 单脉冲测试开始 ===");
    i_din = 2'b01;      // 输入单个正脉冲
    #20;                // 脉冲宽度20ns
    i_din = 2'b00;      // 回到0
    #200;               // 观察脉冲响应
    
    // 5. 双脉冲测试
    $display("=== 双脉冲测试开始 ===");
    i_din = 2'b01;      // 第一个正脉冲
    #20;
    i_din = 2'b00;
    #30;
    i_din = 2'b01;      // 第二个正脉冲
    #20;
    i_din = 2'b00;
    #200;
    
    // 6. 负脉冲测试
    $display("=== 负脉冲测试开始 ===");
    i_din = 2'b11;      // 负脉冲
    #20;
    i_din = 2'b00;
    #200;
    
    // 7. 交替脉冲序列测试
    $display("=== 交替脉冲序列测试开始 ===");
    repeat(5) begin
        i_din = 2'b01;  // 正脉冲
        #20;
        i_din = 2'b00;
        #30;
        i_din = 2'b11;  // 负脉冲
        #20;
        i_din = 2'b00;
        #30;
    end
    
    // 8. 最后回到零点
    $display("=== 测试结束,回到零点 ===");
    i_din = 2'b00;
    #200;
    
    $display("=== 仿真结束 ===");
    $finish;
end

// 监控输出变化
always @(posedge i_clk) begin
    $display("Time=%0t ns, Input=%d, Output=%d", $time, $signed(i_din), $signed(o_dout));
end

endmodule

进行仿真即可,不会仿真的可以问AI

可以看到仿真的结果。

工程文件:https://download.csdn.net/download/fantasygwh2015/92261208

相关推荐
brave and determined10 小时前
可编程逻辑器件学习(day3):FPGA设计方法、开发流程与基于FPGA的SOC设计详解
嵌入式硬件·fpga开发·soc·仿真·电路·时序·可编程逻辑器件
Wishell20151 天前
FPGA教程系列-Vivado IP核之乘法器解析
仿真
材料科学研究5 天前
先到先得!深度学习有限元仿真!
深度学习·cnn·abaqus·仿真·有限元
沐欣工作室_lvyiyi13 天前
用于电动汽车的永磁同步电机调速系统建模与仿真(论文+)
matlab·仿真·永磁同步电机·无传感器
康谋自动驾驶13 天前
如何闭环自动驾驶仿真场景,实现从“重建”到“可用”?
自动驾驶·仿真·建模·3dgs
Altair澳汰尔22 天前
新闻速递丨Altair 与 Gordon Murray Group 携手开发创新超级轻量化平台
汽车·仿真·cae·hyperworks·汽车轻量化·c123
尤利乌斯.X1 个月前
复杂网络仿真从入门到精通:0 学习路线
网络·学习·matlab·仿真·复杂网络
凌盛羽1 个月前
将Gowin高云FPGA仿真库导入Modelsim中并编译
单片机·fpga开发·仿真·modelsim·gowin
熊猫_豆豆1 个月前
MATLAB画出湖面波纹相遇所形成的现象
开发语言·matlab·仿真