在 FPGA 实现 FIR 滤波器时,最常用的是直接型结构,简单方便,在实现直接型结构时,可以选择串行结构/并行结构/分布式结构。
并行结构即并行实现 FIR 滤波器的乘累加操作,数据的处理速度较快,使用多个乘法器同时计算乘法操作,数据输入速率可以达到系统处理时钟的速率,且与阶数无关(相比较串行,用了更多的资源,但提高了处理速度,典型的“以资源换速度”的设计思想);
1. 新建工程和文件
(1) 新建 Verilog 文件
输入信号 16-bit,输出信号 16-bit,复位 rst_n 低电平进行复位;
(2) 获取滤波器系数 h0 ~ h7;
按照 第一讲的方式使用 matlab 的 fdatool 工具箱设计 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(注意指定为有符号数);
(3) 加权求和进行滤波
FIR 滤波器的输出是输入信号不同延时阶段的数据和滤波器系数的卷积(乘累加操作,先做多组乘法,再把乘法的积累加起来),也相当于每个输入延时数据有不同的权值,进行加权和;
按照上面的结构框图,先做 8 次乘法,再把乘法的积相加;
2. 使用 matlab 产生仿真信号
参数:抽样频率 Fs = 32 MHz,信号 f1 = 0.5 MHz,信号 f2 = 13 MHz,具体参见第三讲;
3. 编写仿真文件testbench
(1)例化模块;
(2)写 initial 块,初始化时钟、复位等;
(3)写 always 块,给出时钟翻转等;
(4)读写 .txt 文件,将 matlab 写好的 .txt 的数据赋给输入,把输出数据写入 .txt 文件给 matlab 分析;
具体见第三讲;
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与FPGA数字滤波器设计(3)—— 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,这是因为滤除了上面的高频噪声,这样的结果符合实际情况;
matlab与FPGA数字滤波器设计(5)—— Verilog 串行 FIR 滤波器
matlab与FPGA数字滤波器设计(4)—— Vivado DDS 与 FIR IP核设计 FIR 数字滤波器系统
matlab与FPGA数字滤波器设计(3)—— Matlab 与 Vivado 联合仿真 FIR 滤波器
matlab与FPGA数字滤波器设计(2)——Vivado调用IP核设计FIR滤波器
matlab与FPGA数字滤波器设计(1)——通过matlab的fdatool工具箱设计FIR数字滤波器
Verilog学习笔记——有符号数的乘法和加法