拓冰建站拓冰建站
首页 / 资讯中心 / 正文

高云FPGA FIR低通滤波器设计:系数生成、IP配置与仿真验证

简介面向FPGA开发学习者这套基于高云FPGA的IP设计实现的FIR低通滤波器工程包覆盖RTL源码编写、IP核配置、仿真验证到工程实现的全流程适合通信工程、电子信息、自动化等专业用于课程设计、毕业设计或项目初期的方案演示。压缩包共125个文件大小33.78MB其中Verilog源文件、工程配置文件、仿真脚本与测试数据一应俱全还附有HTML版报告和详细Word文档方便对照工程结构和算法原理进行系统性学习。整个设计已在开发环境中测试通过文档包含滤波器参数选取思路、IP核调用步骤、仿真波形分析方法与常见问题处理能够帮助读者快速上手高云FPGA开发并独立完成同类数字信号处理设计。当前已有84人学习下载适合需要完整工程参考和相关文档支撑的开发者直接使用或在此基础上扩展功能作为课设、毕设的初始框架也很合适。1. 从频谱混叠到高云FPGAFIR低通滤波器为什么值得自己搭一遍ADC采集真实信号时最容易翻车的是“时域看着干净、频域全是毛病”。50 MHz采样率下前端电路引入20 MHz以上的自激振荡示波器上只看到一条细线FFT之后频谱里却多出一截完全不该有的毛刺。FIR低通滤波器的任务就是把这截窗外频谱压到噪声底以下。选高云FPGA而不是DSP方案是因为滤波之后往往还挂着FFT、DDC、成型滤波等逻辑在数据通路上直接内嵌FIR IP核延迟固定、时钟可控布局布线时的时序约束也好写得多。这份基于高云FPGA IP设计的FIR低通滤波器工程内部包含coeff.dat系数文件、sin_wave.dat测试激励、lab_fft.cst物理约束以及详细设计文档价值在于把“系数怎么算、IP怎么配、结果怎么验”整条链路串在一起。适合通信工程、电子信息、自动化专业的课设和毕设参考也适合刚接触FPGA的工程师用来熟悉高云IP集成流程和工程文件体系。2. FIR低通滤波器设计指标与coeff.dat系数生成的完整链路2.1 采样率、截止频率与抽头数的换算逻辑FIR滤波器最上层的三个参数是采样率、截止频率和抽头数三者之间不是独立选取的关系。采样率决定奈奎斯特频率截止频率定义通带与阻带的分界。以50 MHz采样率、5 MHz目标截止频率为例数字域归一化频率就是5/500.1直接代入滤波器设计函数得到的是以奈奎斯特频率为基准的比值而不是把5 MHz直接当参数传进去。抽头数决定过渡带的宽窄。Hamming窗的过渡带经验公式为3.3/N这里N是抽头数得到的值需要再乘采样率换算成实际频率宽度。64抽头对应过渡带约2.6 MHz意味着从5 MHz开始滚降到7.6 MHz左右阻带衰减才达到设计值。如果系统要求在6 MHz处就必须压到-50 dB64阶就hold不住需要上128甚至256阶。所以阶数不是拍脑袋定的是从“过渡带多窄、阻带要多深”倒推出来的。系数阶数还会直接影响硬件开销。高云FPGA里FIR IP核默认用乘加器实现卷积每级阶数对应一组乘法器。64阶在GW1N系列这类入门级器件上资源余量充足但如果后面还挂着FFT、CIC或成型滤波就要注意乘法器资源是否被多个IP共用。一个容易被忽略的点是输入的8 bit数据做乘加后中间结果位宽会膨胀到几十bitIP核内部会按配置的系数位宽和输入位宽自动截断这部分行为要跟踪好否则输出数据精度和仿真对不上。2.2 理想低通滤波器的工程近似与窗函数选型严格意义上的理想低通滤波器单位脉冲响应是无限长的sinc函数并且是非因果的工程上只能通过截断来实现近似。截断就是给无限长脉冲响应乘一个有限长窗FIR滤波器的本质就是“有限长度逼近理想幅频特性”。窗函数直接决定逼近的质量。窗函数主瓣宽度阻带衰减过渡带宽度δf×N/fs适用场景矩形窗最窄约21 dB0.9对阻带无要求Hanning较窄约44 dB3.1通用滤波Hamming中等约53 dB3.38 bit数据场景Blackman较宽约74 dB5.5高精度计量对这个项目来说8 bit ADC的量化噪声本身约48 dBHamming窗的53 dB阻带衰减刚好比量化噪声高出一截。Blackman虽然阻带更深但过渡带宽到5.5/N同样的64阶会吃掉更多有效带宽代价不划算。矩形窗衰减只有21 dB在ADC场景里基本等于没滤波可以用但不可用。2.3 Python生成64阶Hamming窗系数并导出coeff.dat系数生成用Python的scipy.signal.firwin这个步骤在MATLAB里做也等价但Python脚本更容易写进自动化流程改参数重跑一遍也快。直接看生成代码import numpy as np from scipy.signal import firwin fs 50e6 # ADC采样率 50 MHz fc 5e6 # 截止频率 5 MHz taps 64 # 抽头数 64 # firwin返回64个浮点系数窗口类型用hamming coeff_f firwin(taps, fc, fsfs, windowhamming) # Q15定点化映射到16bit有符号整数区间 coeff_q15 np.round(coeff_f * 32767).astype(np.int16) # 导出为两列文本格式第一列索引第二列系数值 with open(coeff.dat, w) as f: for idx in range(taps): f.write(f{idx:04d} {coeff_q15[idx]:6d}\n) print(f完成共导出{taps}个抽头到coeff.dat)这段代码里firwin的第一个参数传taps第二个传fc但如果没有fs50e6这个参数scipy会把fc当作归一化频率处理算出来的系数在信号链上完全错位。输出到coeff.dat的每一行是“索引 系数值”索引占四位宽度系数值占六位宽度。高云FIR IP核的系数导入界面支持直接读文本文件按空格切分后逐行映射到抽头如果你的高云版本要求.coe格式把文件头改成Radix10; Coefficient_Width16;再导出即可。Q15定点化的含义是把范围在[-1,1)内的浮点系数乘32767后取整让系数落在16 bit有符号整数的有效区间里IP内部乘法器按这个位宽做对齐量化后系数值如果超过32767就说明系数计算有误。2.4 Q15定点量化对阻带衰减的影响定点量化不是无代价的。浮点系数转成Q15后每个系数会引入约正负0.5的量化误差。如果相邻抽头的量化误差正负交替折叠到频域后会在阻带形成起伏严重时阻带衰减掉10 dB以上。所以在做IP核配置之前建议先对比一次浮点和定点系数的幅频响应。对比方法不复杂用scipy.signal.freqz分别计算浮点系数和Q15系数的频率响应取阻带起始频点比较衰减值。如果衰减差超过3 dB就需要把系数位宽提升到18 bit或20 bit。对8 bit ADC输入来说16 bit系数已经足够因为系数量化噪声远低于ADC自身量化噪声再增加位宽对系统SNR的提升非常有限但综合面积却线性增长。提示系数一旦导入IP核并完成综合再次修改就要重新走一遍布局布线。把定点量化检查放在生成阶段比在硬件上反复试错省时间得多。3. 高云FPGA的FIR IP核配置与lab_fft工程文件结构解析3.1 云源软件里FIR Filter IP核的参数配置高云云源软件的IP Core Generator里FIR滤波器IP一般归类在DSP目录下。新建工程后从IP核生成器进入FIR Filter配置界面里需要重点关注的参数项如下参数项推荐值说明Filter TypeLow Pass低通滤波器Input Data Width8匹配ADC输出位宽Coefficient Width16与coeff.dat的Q15格式对应Coefficient Filecoeff.dat从文件导入系数Number of Taps64与系数文件的行数一致Decimation Rate1不抽取保持原始采样率配置时两个地方最容易出错。第一是Coefficient Width选成8 bitIP核会按8 bit解析coeff.dat中的数据做大数截断后系数全部溢出综合时直接报数据宽度不匹配。第二是Number of Taps必须和coeff.dat里的系数行数完全一致多一行或少一行都会在IP生成时提示文件格式错误。高云IP核没有自动感知系数数量的功能导入之前先对系数文件做一次行数检查是值得养成的习惯。3.2 lab_fft.db、device.cfg与lab_fft.bin的文件职责解压工程压缩包后看到一堆后缀陌生的文件容易让人觉得工程损坏了实际上每个文件都有明确职责。lab_fft.db是高云软件的工程数据库保存综合、布局、布线之后的完整网表与约束信息。修改Verilog代码后靠这个文件做增量编译能省掉全量综合的时间如果工程被复制到别的机器上缺少.db文件并不会导致无法打开工程只是必须从头Run一遍完整流程。device.cfg是下载器用的目标器件配置文件里面记录目标FPGA型号、封装、烧写接口和擦除选项。烧录工具先读device.cfg确认芯片匹配再加载bitstream芯片型号不对会直接拒绝烧写。lab_fft.bin是布局布线后导出的比特流文件真正下载到FPGA里的就是它。高云FPGA的配置SRAM是易失的每次上电都要重新写bin文件。lab_fft.binx是伴随bin生成的辅助二进制文件存的是编译时间和工程版本之类的元数据高云IDE重新打开工程时会拿它做版本校验但烧录流程用不到。整个依赖链可以概括为cst约束和Verilog源码经过综合布局布线生成db再由db导出bin和binx而device.cfg只服务于最终下载环节。3.3 Verilog顶层例化与IP核复位时序IP核生成后高云会同时生成一个例化模板。把FIR滤波器放进顶层数据通路代码比用Xilinx时的等价代码短不少// lab_fft_top.v module lab_fft_top( input wire clk_50m, input wire rst_n, input wire [7:0] adc_din, output wire [15:0] fir_dout ); // Gowin FIR低通滤波器IP核例化 Gowin_FIR_LP u_fir_lp( .clk (clk_50m), .reset (~rst_n), .clken (1b1), .din (adc_din), .dout (fir_dout) ); endmodule逻辑说明adc_din是ADC采样输入fir_dout是滤波后的16 bit输出。reset信号用了低有效向高有效转换的方式对外部rst_n取反后送入IP核。实际调试中需要注意reset必须完成一次有效的低脉冲再释放高云FIR IP在启动时如果不复位内部系数加载状态机可能停留在未知状态前几个时钟周期的输出会出现不确定值。clken时钟使能端在这里固定拉高如果后续需要低功耗控制可以通过拉低clken来停止数据计算但输出不会保持需要注意下游模块的空闲处理。4. 从sin_wave.dat到lab_fft.cst仿真激励、物理约束与在线验证4.1 Testbench回放sin_wave.dat采样数据sin_wave.dat里存放的是预先生成的正弦波采样点。仿真时用系统函数读取文件回放采样点比在testbench里用signal generator实时生成更高效因为数据在仿真开始时一次性装载可以精确控制每个时钟周期喂给IP核的值。timescale 1ns/1ps module tb_fir_lp(); reg clk_50m 1b0; reg rst_n 1b0; reg [7:0] adc_din 8d0; wire [15:0] fir_dout; // 50 MHz时钟周期20 ns always #10 clk_50m ~clk_50m; reg [7:0] sample_mem [0:2047]; integer idx; initial begin // 读取十六进制格式采样数据 $readmemh(sin_wave.dat, sample_mem); #50 rst_n 1b1; for (idx 0; idx 2048; idx idx 1) begin (posedge clk_50m); adc_din sample_mem[idx]; end repeat(64) (posedge clk_50m); $finish; end lab_fft_top u_dut ( .clk_50m (clk_50m), .rst_n (rst_n), .adc_din (adc_din), .fir_dout(fir_dout) ); endmodule这里需要特别关注$readmemh与文件格式的匹配关系。sin_wave.dat如果是十六进制文本用$readmemh才正确如果是二进制或十进制则要用$readmemb或者自定义读文件逻辑。读取结束后额外跑64个周期是为了让滤波器流水线把最后一批数据从寄存器里推出来否则波形末尾会显示不完整。2048个采样点已经足够覆盖64阶滤波器从启动到稳定的完整瞬态不需要把文件里所有数据都回放一遍。4.2 lab_fft.cst物理约束引脚、电平标准与时钟周期lab_fft.cst是物理约束文件高云的cst语法与Xilinx UCF明显不同每条约束以分号结尾。引脚约束的写法如下IO_LOC clk_50m 24; IO_PORT clk_50m IO_TYPELVCMOS33 PULL_MODEUP; IO_LOC rst_n 25; IO_PORT rst_n IO_TYPELVCMOS33 PULL_MODEUP; IO_LOC adc_din[0] 26; IO_LOC adc_din[1] 27; IO_LOC adc_din[2] 28; IO_LOC adc_din[3] 29; IO_PORT adc_din[0] IO_TYPELVCMOS33; IO_PORT adc_din[1] IO_TYPELVCMOS33;约束规则里的IO_LOC决定引脚编号IO_PORT的IO_TYPE指定电平标准。上面的24、25、26这些引脚号要替换成实际PCB原理图里对应的FPGA引脚直接照抄会导致管脚复用冲突。PULL_MODEUP给复位按键提供默认上拉如果板子上已经有外部上拉电阻这个属性可以省略。电平标准是另一个容易踩的坑LVCMOS33对应3.3 V IO如果板子IO供电是2.5 V却配成3.3 V时序报告不会报错但实际采集信号时会出现偶发bit翻转。时钟周期约束建议单独写在cst文件末尾create_clock -name clk_50m -period 20.000 [get_ports {clk_50m}]这里period的值必须大于等于实际时钟周期20 ns对应50 MHz。如果综合后时序不收敛优先检查period是否被误写成了2 ns或者20 ns不带小数这两个写法都可能让时序分析工具的时钟裕量计算错乱。4.3 仿真结果判断群延迟固定性与阻带抑制比仿真播放波形后同步观察adc_din和fir_dout两组信号。FIR滤波器有固定群延迟64阶滤波器的群延迟是31.5个时钟周期换算到50 MHz下就是630 ns。如果dout相对din的延时不是这个值的整数倍说明IP核内部流水线插入了一级或多级附加寄存器这是正常现象只要延时在多次运行中保持不变数据链路就是确定的。验证滤波器性能时建议在sin_wave.dat里混合两个频率分量比如2 MHz有效信号叠加24 MHz干扰。滤波后2 MHz分量幅值应该基本不变24 MHz分量衰减到原来的百分之一以下。如果阻带衰减达不到预期优先检查coeff.dat导入时是否有系数位宽截断以及IP核内部是否选择了错误的输入输出截断策略。另一个常见问题是输入接近满幅时溢出8 bit输入给满量程127的正弦波FIR输出16 bit也接近饱和测试激励适当降低输入幅度给滤波器留出至少6 dB余量。5. lab_fft.bin烧录与调参技巧把FIR工程改成多频信号滤波器5.1 用cos_sin_lut.dat做片内正交信号源cos_sin_lut.dat是预计算的cos和sin查找表数据工程里很多人在用波形发生器扫频验证时才发现它的价值。把它加载到FPGA内部ROM通过相位累加器就可以生成两路正交波形不需要外部仪器灌信号。reg [9:0] phase; reg [7:0] sin_out; reg [7:0] cos_out; always (posedge clk_50m) begin phase phase 10d1; end always (posedge clk_50m) begin sin_out sin_lut[phase]; cos_out cos_lut[phase]; end这里相位增量控制输出频率增量每加1输出频率提升约48.8 kHz。这个数字来自50 MHz采样率除以1024点查找表深度。想要验证多频混合信号把sin_out和cos_out分别乘上不同系数再相加就得到两个频率叠加的测试信号直接接在FIR输入端。比起外部信号发生器这个方案的频率偏差全部来自晶振精度和实际工作时钟完全同源不会出现仿真和板上频率对不上的问题。5.2 分布式算法结构替换与参数迭代顺序如果目标是降低DSP资源占用高云FPGA上的FIR还可以考虑分布式算法结构。分布式算法用查找表替代乘法器在乘法器资源紧张但LUT富余的型号上64阶FIR可以节省大量DSP块。代价是查找表位宽随阶数指数增长需要按4 bit一组做拆分控制逻辑会更复杂一点。这个对于资源评估非常有参考价值先确认当前FPGA够不够放乘法器不够再考虑DA结构不必在一开始就为了省资源把设计搞复杂。调参时不要同时动两个参数。先改截止频率把fc从5 MHz改成8 MHz观察通带边缘是否按预期右移再改阶数从64加到128观察阻带衰减是否下降得更快。coeff.dat每修改一次就需要重新生成lab_fft.bin此时务必删除lab_fft.db后重新Run All让工具走完整综合布局布线流程避免增量编译时旧系数残留。验证通过后把输入混频结果和滤波输出同时送到调试探针对比两个端口的FFT频谱确认阻带抑制比满足设计指标再进入下一轮参数迭代。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门