
简介基于高云FPGA的IP设计实现FIR低通滤波器是一份面向毕业设计、课程设计、工程实训与学科竞赛的完整工程资源。项目以硬件描述语言与IP核配置为主线涵盖源码、工程文件、约束与仿真脚本从滤波器系数生成、正弦波/余弦波查找表、IP例化到仿真验证逐层展开便于读者快速搭建并复现功能。资源包为ZIP压缩格式共124个文件约33.77MB以Verilog/VHDL源文件、高云FPGA工程与IP核配置prj/qdb/ipc为核心辅以仿真波形、测试数据、约束文件、日志及设计说明文档目录结构清晰便于按模块查阅。目前已有154人浏览学习内容经严格测试运行正常答辩评审平均分达96分适合中等及以上基础的学习者直接复刻也可作为设计报告与扩展开发的基础。除项目本身外遇到使用问题可联系作者获得答疑并可获取相关开发工具与学习资料支持。1. 高云FPGA上的FIR低通滤波器先确认IP核是不是最优解做数字信号处理课程设计时我在高云GW1NR-4上实现过一段FIR低通滤波一开始坚持手写卷积和加法树结果综合后最高频率只跑到70 MHz离100 MHz目标差很远。后来换成高云IP核把系数文件coeff.dat填进配置界面半小时内就完成综合、仿真和频谱验证。这个工程包就是这样的完整流程用Gowin FPGA Designer例化FIR低通滤波器再配合FFT IP核对滤波前后的信号做频谱对比源码、约束文件、系数文件和测试数据都在同一个压缩包里。毕设答辩、课程设计、大作业或竞赛复刻时直接打开工程就能复现完整环节。下面把FIR系数生成、IP核配置参数、FFT验证方法和排错路径拆开讲新手可以照着做有经验的人也能看到一些容易忽略的细节。2. FIR低通滤波器设计原理与系数生成FIR滤波器在FPGA上的实现结构是一条抽头延迟线输入信号经过N-1个寄存器依次延迟每个延迟输出与对应系数相乘再把所有乘积累加。输出表达式可以写成y[n] h[0]x[n] h[1]x[n-1] ... h[N-1]x[n-N1]其中h[k]是滤波器系数x[n-k]是输入x经过k个时钟周期延迟后的值。只要系数满足h[k] h[N-1-k]滤波器就具备严格线性相位信号通过后不会发生相位失真。这个性质在音频、心电和通信基带处理里很重要所以FPGA工程里FIR比IIR更常用。2.1 从理想低通到窗函数设计理想低通的频率响应是门函数逆变换回时间域是无限长的sinc函数无法直接实现。工程上通常用有限长的窗函数截断sinc得到一组可综合的h[k]。窗口越长过渡带越窄但阻带衰减主要由窗型决定而不是窗长。下表列出几种常见窗函数在相同抽头数下的近似表现这里的fs是采样率N是抽头数窗函数过渡带宽度近似阻带衰减矩形窗0.9 × fs / N21 dB汉宁窗3.1 × fs / N44 dB汉明窗3.3 × fs / N53 dB布莱克曼窗5.5 × fs / N74 dB选择哪个窗要看系统要求。通信基带通常要求阻带衰减大于50 dB我一般用汉明窗如果只是去掉直流偏置或工频干扰矩形窗就够了还能减少乘法器资源消耗。2.2 用Python生成coeff.dat系数文件高云FIR IP核从文本文件读取系数常见格式是每行一个十六进制有符号数。这个工程里的coeff.dat就是由下面这段Python脚本生成的采样率100 MHz截止频率10 MHz抽头数17from scipy.signal import firwin import numpy as np fs 100e6 # 采样率单位Hz cutoff 10e6 # 截止频率单位Hz ntaps 17 # 抽头数阶数16 # 设计汉明窗低通系数返回归一化浮点系数 coeff firwin(ntaps, cutoff, fsfs, windowhamming) # 量化到16位有符号整数 coeff_q np.round(coeff * 32767).astype(np.int16) # 写为高云IP核需要的十六进制行文本 with open(coeff.dat, w) as f: for c in coeff_q: f.write(f{c 0xffff:04x}\n) print(coeff_q)脚本里firwin的cutoff参数直接传10e6配合fs100e6会自动完成归一化。量化时先乘以32767再四舍五入把浮点系数映射到16位有符号范围。写入文件时对负数做按位与0xffff得到补码的十六进制表示高云IP核读进来后仍然按有符号数解释。这里最容易踩的坑是直接用Python的hex()输出负数会得到带负号的字符串导致IP核解析失败。我在生成这个工程时也踩过一次后来才改成掩码写法。2.3 自己写RTL卷积 vs 调用高云IP核手动实现FIR需要自己做乘法器、加法树和多级流水线。以17抽头为例至少需要17个乘法器和16个加法器还要决定在哪一级插入寄存器才能满足100 MHz时序。高云IP核把这些全部封装好了配置界面只需要填数据位宽、系数位宽和抽头数它会自动把乘法器映射到DSP单元综合工具也更容易收敛。也有人说IP核浪费资源但实际在GW1NR-4上查资源报告17抽头16位FIR只占用少量DSP块LUT消耗主要是输入输出打拍。对课程设计和毕设来说用IP核最大的收益是稳定、可追溯答辩时还能用IP核数据手册支撑设计合理性。3. Gowin工程文件结构与IP核配置拿到工程包解压后先不要急着点综合。文件列表里的_info、_vmake以及lab_fft.bin、lab_fft.binx、device.cfg、lab_fft.cst、lab_fft.db都是从Gowin FPGA Designer工程目录直接拷贝出来的coeff.dat和sin_wave.dat是运行时要读取的数据文件。下面这张表列出主要文件的作用文件作用lab_fft.bin布局布线后生成的比特流文件lab_fft.binx高云工程数据库文件封装了器件信息device.cfg器件配置记录FPGA型号和封装lab_fft.cst管脚约束和时钟约束文件coeff.datFIR低通滤波器系数十六进制文本sin_wave.dat测试用输入信号采样点cos_sin_lut.datFFT IP核的旋转因子ROM初始化文件lab_fft.db综合后的工程数据库文件从这个文件组成能看出来工程在FIR之外还挂了一个FFT IP核目的是做频域验证。sin_wave.dat提供混合频率信号FFT负责看频谱FIR负责把带外分量滤掉。3.1 工程打开与器件确认Gowin FPGA Designer打开工程时一般通过File - Open Project选择.gprj或.ddb文件。如果工程目录下没有这些后缀可以直接定位到device.cfg所在目录软件会自动识别工程结构。打开后第一件事是查看当前器件型号是否和你的开发板一致。这个工程如果是在GW1NR-4上做的换成GW1N-1后资源可能不够FFT和FIR例化需要重新映射。器件不同lab_fft.cst里的管脚位置也要重新分配。3.2 FIR IP核参数配置表在IP Core Generator中选DSP - FIR Filter参数按下表设置参数项配置值说明Coefficient Filecoeff.dat系数文件需与工程目录一致Input Data Width16输入信号位宽Coefficient Width16系数位宽Number of Taps17抽头数Filter TypeLow Pass低通滤波器Interpolation / DecimationNone单速率模式Pipeline SchemeAuto100 MHz下自动流水Clock Rate100 MHzFIR工作时钟Sample Rate100 MHz输入采样率Clock Rate和Sample Rate在单速率模式下通常设置成相同值。如果采样率与时钟不同步IP核内部会按比例调整有效抽头计算但绝大多数低通场景不会用到多速率模式所以这里保持100 MHz即可。3.3 管脚约束与时钟约束lab_fft.cst中包含IO_LOC管脚位置和IO_PORT电平标准。下面是一段与开发板匹配的约束示例实际管脚号必须查开发板原理图不能照抄IO_LOC clk 52; IO_PORT clk IO_TYPELVCMOS33; IO_LOC rst_n 12; IO_PORT rst_n IO_TYPELVCMOS33 IO_PULL_UPON;把管脚约束放到工程后还要在同样位置加上时钟约束。高云支持SDC常见写法是create_clock -name clk -period 10.0 [get_nets {clk}]period 10.0对应100 MHz时钟。如果开发板上的晶振是50 MHz要改成20.0否则时序分析会误报违例。我在第一次移植工程时踩过这个坑后来才发现是高估了实际时钟频率。3.4 在顶层模块例化FIR IP核生成IP核后顶层模块只需要做一次简单例化。端口名称以IP核生成的模板为准下面是一段简化版本用来展示数据流连接方式module top ( input wire clk, input wire rst_n, input wire signed [15:0] din, output wire signed [15:0] dout ); fir_lowpass u_fir ( .clk (clk), .reset_n (rst_n), .clken (1b1), .din (din), .dout (dout) ); endmoduleclken固定为1表示每个时钟周期都执行滤波。如果后续想降低功耗可以用一个低频clken让滤波器只在必要时工作。din和dout都声明为signed这是FPGA上正确处理负数的前提。如果只写wire [15:0]综合器会把它当无符号数输出会叠加上一个直流偏置FFT之后会看到一根很大的0 Hz分量。4. FFT频谱验证FIR低通滤波器的实际效果单独看时域波形很难判断滤波器是否达标所以工程里用FFT作为验证链路。sin_wave.dat里存的是一段16位采样数据由10 MHz和25 MHz两个正弦叠加而成采样率100 MHz。低通截止频率设在10 MHz理想情况下25 MHz分量衰减超过40 dB10 MHz分量保持不变。4.1 用$readmemh把测试数据送进FIR功能仿真时不能靠手工敲输入数据工程已经把波形放在sin_wave.dat。在Testbench里用$readmemh把它读入寄存器数组再按时钟节拍逐个读出。下面是可用的Testbench核心代码reg signed [15:0] sin_mem [0:1023]; reg [9:0] addr; reg signed [15:0] din; initial begin $readmemh(sin_wave.dat, sin_mem); end always (posedge clk or negedge rst_n) begin if (!rst_n) begin addr 10d0; din 16sd0; end else begin addr addr 10d1; din sin_mem[addr]; end end当addr递增到1023后会自动回卷于是FIR输入变成循环波表。这样做的目的是避免手写一大堆初始化数据。需要注意$readmemh要求文件里是十六进制数允许注释和空行但如果有非法字符仿真器会报错。4.2 FFT参数设置与cos_sin_lut.dat的配合cos_sin_lut.dat是FFT IP核的旋转因子初始化文件里面预先算好了256点FFT所需的余弦和正弦值。配置FFT IP核时把Twiddle ROM路径指向这个文件它会自动转换成ROM初始化内容。下面是我在工程里验证时使用的FFT配置参数项配置值说明FFT Length256点数Data Width16输入数据位宽Twiddle ROMcos_sin_lut.dat旋转因子初始化文件ArchitecturePipelined流式结构吞吐高ScalingBlock Floating Point自动缩放防止溢出256点FFT的频率分辨率是100 MHz / 256 390.625 kHz10 MHz对应第26根谱线25 MHz对应第66根谱线两者相距40根谱线频谱泄露影响很小。如果改用64点FFT分辨率会变成1.5625 MHz虽然也能区分两个频点但幅度精度会变差。所以毕设和竞赛项目里我更推荐保留256点。4.3 如何快速判断滤波是否生效把din和dout分别接入两个FFT模块或者分时复用同一个FFT模块在仿真结束后比较输出频谱。我实测得到的结果是10 MHz频点幅度下降小于1 dB25 MHz频点幅度下降约46 dB与汉明窗设计理论值基本一致。如果滤波结果与预期不符不要急着改滤波器参数先检查FFT输出是否饱和。Block Floating Point模式会自动移位但输入信号接近满幅时仍可能溢出。此时在FFT之前手动把数据右移两位频谱会恢复正常。5. 把FIR IP核调出更好效果的几个实用技巧基础功能跑通后可以在现有工程上继续做优化下面几个方向对竞赛和毕设都很有用。5.1 用系数重配置实现动态低通/高通高云FIR IP核支持可重配置系数接口。系统运行过程中通过状态机或串口更新系数就能把同一个IP核从低通切到带通。常见做法是把多组系数存在ROM里每组对应一个截止频率再用按键或上位机选择。需要注意重配置接口的时序要求切换系数时最好等当前数据帧处理完否则输出会有一个时钟周期的毛刺。5.2 量化噪声和截位处理FIR输出位宽比输入位宽多出约log2(N)位17抽头意味着最多增加5位。如果直接把dout截断到16位低5位的量化噪声会叠加到频谱上。更合理的方式是保留更多位宽在后续模块里再做饱和处理。高云IP核配置里如果把Output Truncate选成Round会引入四舍五入效果比直接截断好1 dB左右。5.3 排错顺序复现高云工程遇到问题时我一般按下面的顺序排查检查coeff.dat的系数个数是否等于Number of Taps多一个换行符也可能让IP核解析出错。检查reset_n是否可靠释放如果复位时间是异步的FIR输出会有一段无效数据。检查顶层端口有没有按模板写全clken悬空和接高电平在综合结果上不一样。用后仿确认时序直接看dout相对clk的延迟是否保持固定。最后提供一个快速验证量化噪声的技巧把coeff.dat改成只有一个0x4000其余全部填0x0000。此时FIR变成纯增益0.5如果仿真输出仍然有非预期波动说明问题出在电源地噪声或后端时钟抖动而不是滤波器设计本身。本文还有配套的精品资源点击获取