在 FPGA 实现 FIR 滤波器时,最常用的是直接型结构,简单方便,在实现直接型结构时,可以选择串行结构/并行结构/分布式结构。
并行结构即并行实现 FIR 滤波器的乘累加操作,数据的处理速度较快,使用多个乘法器同时计算乘法操作,数据输入速率可以达到系统处理时钟的速率,且与阶数无关;
1. 新建工程和文件
(1)新建 Verilog 文件
输入信号 16-bit,输出信号 16-bit,复位 rst_n 低电平进行复位;
(2)获取滤波器系数 h0 ~ h7;
按照 第一讲 通过matlab的fdatool工具箱设计FIR滤波器 的方式使用 matlab 设计 FIR 低通滤波器,设置为系数 8-bit 量化,采样时钟 32 MHz(并行处理时输入输入速率可以达到系统时钟速率),截止频率设为 1 .5 MHz,与前面 调用 IP 核的时候 一致(32 MHz时钟,0.5MHz信号 + 5 MHz 高频噪声,99阶);
观察右上方的幅频特性曲线,发现 7 阶的滤波器效果确实不好,在 5 MHz处幅度衰减较小,所以此处更改噪声为 13 MHz,该频率点的衰减较大,滤波效果明显;
量化后导出参数,可以直接用 .coe 文件导出备用,导出后 matlab 也会自动打开系数文件,用 Verilog 语言的常数定义参数 h0 ~ h7(注意指定为有符号数);
此外,除了将量化后的系数导出为 .coe 文件之外,还可以把系数用小数形式 导出到 Matlab 的工作空间 ,点击 File->Export ,弹出 export 对话框 ,导出名字为 Num 。
导出到 工作空间 后,在命令行窗口输出 Num 变量名 ,回车,输出 Num 的值,把这些值 复制黏贴 到 Matlab 代码的 hn 部分,用中括号[]括起来 ,就可以运行 Matlab程序了。
(3)加权求和进行滤波
FIR 滤波器的输出是输入信号不同延时阶段的数据和滤波器系数的卷积(乘累加操作,先做多组乘法,再把乘法的积累加起来),也相当于每个输入延时数据有不同的权值,进行加权和;
按照上面的结构框图,先做 8 次乘法,再把乘法的积相加;
2. 使用 matlab 产生仿真信号
参数:抽样频率 Fs = 32 MHz,信号 f1 = 0.5 MHz,信号 f2 = 13 MHz,具体参见 第三讲 Matlab 与 Vivado 联合仿真 FIR 滤波器 ;
3. 编写仿真文件testbench
(1)例化模块;
(2)写 initial 块,初始化时钟、复位等;
(3)写 always 块,给出时钟翻转等;
(4)读写 .txt 文件,将 matlab 写好的 .txt 的数据赋给输入,把输出数据写入 .txt 文件给 matlab 分析;
具体见
matlab与FPGA数字滤波器设计(3)—— Matlab 与 Vivado 联合仿真 FIR 滤波器mp.weixin.qq.com/s/m2xXxyb1xO3K8T3HOnIxYQ
4. 仿真
(1)Verilog 仿真
可以看到,高频噪声基本被滤除,但是肉眼能观察出波形与标准正弦波有一定差距;
(2)Matlab仿真
Matlab仿真,分别是 f1、f2、f1+f2、滤波后的数据;
使用 matlab 做 FFT 进行频谱分析,使用 7 阶(8个系数)FIR 滤波器能够很好的保留低频 0.5 MHz 信号,滤除高频 13 MHz 信号;
(3)综合的 RTL 图
综合后共用到 6 个乘法器和 7 个加法器, Verilog 共计有 8 次乘法,但是其中有 2 个乘法的乘数是常数 0,所以 Vivado 只综合出 6 个乘法器;
与串行的对比,下图为串行 FIR 滤波器的 RTL 图:
5. 截位输出部分更改
还是看这张图,在对输入的 16-bit 数据做运算后,为了保证数据不溢出,得到的结果位宽逐渐变大,但是最后输出又是 16-bit,此时需要对数据进行截位( 如果不截位,那么当一个数字信号处理系统较复杂的时候,数据的位宽会非常大,在处理中时不现实的 ),
当对本例中的 32-bit 的数据进行截位时,从哪里开始截取是一个经常会遇到的问题:
(1)截取高 16-bit (data_out_temp[31:16]),当数据比较大的时候可以这样做(高位上都是有效数据,用十进制举例 9*9 = 81,取十进制高位近似为 80,类比到二进制),这样相当于损失了一些低位的精度;
(2)截取低 16-bit (data_out_temp[15:0]) ,当数据比较小的时候可以(高位上没有有效数据,用十进制举例 2 * 2 = 4,取十进制低位为 4);
(3)根据仿真出来的数据的表示范围,去掉高位的符号位,截取实际需要的数据;
需要对 data_out_temp[31:0] 截位(先截高 16 位作为 data_out 看波形),所以在仿真中先把该信号添加到波形显示窗口,该信号是一个内部信号,没有在输出端口,按照下图找到 testbench 仿真例化的器件,找到下方的 data_out_temp 信号并右键 Add to Wave Window(箭头1),点击 Restart(箭头2)之后再仿真 Run(箭头3),调成模拟波形 Analog(具体参见 Matlab 与 Vivado 联合仿真 FIR 滤波器);
按照下图箭头所示展开信号,可以看到 data_out_temp 信号的 23 ~ 31 bit 都是一样的,代表符号位,0 代表正数,1 代表负数,实际上只需要 1 位符号位代表正负即可,可以取 data_out_temp[23:8] 这 16 位;
选中 data_out_temp[23:8] 这 16 位后 右键 新建虚拟总线(New Virtual Bus),类似的,把 data_out_temp[22:7] 也新建成虚拟总线(New Virtual Bus 1);
可以看到,data_out_temp[23:8] 的波形并没有受到影响,data_out_temp[23:8] 的波形已经不能体现 data_out_temp 的特性,所以可以截取 data_out_temp[23:8] 作为 data_out;
assign data_out = data_out_temp[23:8];
如下图,使用 data_out_temp[23:8] 作为 data_out 后,在黄线时刻滤波输出值为 16619,和输入信号 data_in 在一个数量级,且小于 data_in,这是因为滤除了上面的高频噪声,这样的结果符合实际情况;
此处波形如果不对的原因还可能涉及到对有符号数的处理问题
https://zhuanlan.zhihu.com/p/342108822 作者:FPGA探索者,