CIC滤波器仿真与实验过程及结果记录

整理于2023-10-08

0.0 前言:

Matlab 设计仿真CIC滤波器

前面介绍了使用matlab中的Filter Designer工具箱进行CIC抽取滤波器设计的仿真过程与结果。下面在前面的基础上针对现有的【正点原子ZYNQ】平台,进行CIC实际设计实验测试。

0.1 平台简介

所使用的演示平台为【正点原子】的领航者ZYNQ开发板以及高速adda模块

1. 实验设计

adc以25MSPS采集一混频(比如:1.25MHz+62.5kHz)信号,经cic抽取滤波后,数据速率降至500kHz,之后由dac输出,通过示波器观察滤波前后波形。

2. matlab 中仿真

步骤:

  1. 生成待滤波的混频信号信号
  2. 生成相应的CIC抽取滤波器(见:CIC滤波器设计仿真)
  3. 进行滤波
  4. 做图对比
matlab 复制代码
%% 生成信号
Fs = 25e6;
Ts = 1/Fs;
Ts2 = 50/Fs;
T = 1e-2;
N = T*Fs;
N2 = T * (Fs/50);
n = (0:N-1)';
n2 = (0:N2-1)';
t = Ts * n;
t2 = Ts2 * n2;
f = Fs/N*n;
R = 50;

f1 = 1.25e6;
f2 = 50e3;
f3 = 125e3;

s1 = sin(2*pi*f1*t);
s2 = sin(2*pi*f2*t);
s3 = s1 + s2;
% 计算混叠频率
Aliasingf = (1 - (f1/(Fs/R/2) - floor(f1/(Fs/2/50))))*(Fs/R/2)

% 使用cic filtersigner 进行50倍抽取滤波
y3 = H_CIC50(s3);         % 信号s3 50倍降采样后得到信号y
y3 = double(y3)/(50^3);	  % 增益调整,此处未进行补偿滤波器设计

仿真计算结果:

上面两幅图为生成的高采样率混频信号以及其频谱;下面两幅图为滤波后信号及其频谱。可以观察到,经过cic抽取滤波器后,高采样率混频信号得到了显著的降采样和滤波效果,且降采样符合预期;但是波形或者说频谱分布,与预期相差较大,怀疑原因是滤波过程有相位延迟造成的数据点移位而引入的,需要进一步确认。

3. vivado 中仿真

版本:vivado2019.1

3.1 vivado 中 CIC ip核调用

参考来源

FPGA数字信号处理(十九)Vivado CIC IP核实现-CSDN博客
xilinx 官方手册:

DS845_cic_compiler

PG140-cic-compiler

IP Catalog 中搜索CIC,打开CIC Compiler,配置cic ip核相关参数。

下图为实现50倍降采样配置。

Filter Options

  1. Filter Specification
  • 滤波器类型 - 抽取 Filter Type : Decimation
  • 滤波器级数 - 3 Number Of Stages : 3
  • 滤波器延迟因子 - 1 Differential Delay : 1
  • 通道数 - 1 Number Of Channels : 1
  1. Sample Rate Change Specification
  • 选择固定采样率倍数,设置为 50

另外,还需注意数据位宽设置,根据所需数据特性设置,最终设计结果可以在 Summary 中查看。

左侧可以选择到频响窗口,观察频响特性

生成CIC ip核后,在主程序中例化调用

verilog 复制代码
 cic_compiler_0 u_cic_compiler_0 (
  .aclk(sys_clk),                              // input wire aclk
  .s_axis_data_tdata(ad_data),               // input wire [7 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(s_axis_data_tvalid),  // input wire s_axis_data_tvalid
  .s_axis_data_tready(),                    // output wire s_axis_data_tready
  .m_axis_data_tdata(cic_out_data),         // output wire [31 : 0] m_axis_data_tdata
  .m_axis_data_tvalid(m_axis_data_tvalid)  // output wire m_axis_data_tvalid
);

3.2 程序设计

本次实验例程主要参考【正点原子】教程中关于高速adda部分的例程。

程序设计顶层原理图:

3.3 仿真结果

编写testbench文件,调用由matlab生成的波形文件作为仿真输入,观察滤波输出。

3.3.1 matlab生成波形文件

参考来源

verilog--使用FIR滤波器IP对数据降采样 - 知乎 (zhihu.com)

基本步骤简述:

  1. 生成波形(如上所述)
  2. 按要求位宽做量化处理
  3. 按16进制写入文件保存

参考程序:

matlab 复制代码
sig2 = s2;						% 输入信号
sig2=sig2/max(abs(sig2));       % 归一化处理
sig2=round(sig2*(2^(N-1)-1));   % N比特量化 此处 N = 8
fid=fopen('fitFliterTestData2.txt','w'); % 写入文件保存
data2_i = real(sig2);			% 按照指数法生成信号时,提取实部信号
data2_q = imag(sig2);			% 按照指数法生成信号时,提取虚部信号
for n=1:length(sig2)			% 进行补码替换操作
    if data2_i(n) <0
       data2_i(n) = 2^N + data2_i(n);
    end
    if data2_q(n) <0
       data2_q(n) = 2^N + data2_q(n);
    end
end
fprintf(fid,'%x \n',data2_i);	% 按照16进制保存文件
fclose(fid);

3.3.2 vivado 中仿真调用外部数据文件

参考内容

vivado仿真 文件读取和写入-小李干净又卫生的博客-CSDN博客

VIVADO仿真读写文件方式_vivado readmemh-CSDN博客

读取文件

$readmemb$readmemh 用来从文件中读取数据到存储器中。其中 readmemb 要求每个数字是二进制数,readmemh 要求每个数字必须是十六进制数字。数字不能包含位宽说明,数字中可以有不定值x或X,高阻值z或Z,和下划线(_),和Verilog语法中的用法是一样的。

一共有下边6种用法:

(1)$readmemb("<数据文件名>",<存储器名>);

(2)$readmemb("<数据文件名>",<存储器名>,<起始地址>);

(3)$readmemb("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);

(4)$readmemh("<数据文件名>",<存储器名>);

(5)$readmemh("<数据文件名>",<存储器名>,<起始地址>);

(6)$readmemh("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);

写入文件

写入文件的操作与C语言类似,首先打开文件,写入数据之后关闭文件。

outputfile = $fopen("file2.txt","w"); 打开文件

$fwrite(outputfile,"%b\n",memory); 写入数据

$fclose(outputfile); 关闭文件

注意:

在写入数据的时候,写入的数据不能是一个数组,必须是单个的数字,因此想写入数组的时候必须要循环单个写入数据,写入数据的数据格式可以是2进制10进制16进制,方式与C语言类似,%控制写入的类型。

代码参考:

verilog 复制代码
//** 数据来源:MATLAB 
//** 函数:$readmemh 【以读取16进制形式读取TXT文件】

    localparam DATA_NUM = 25000; // 数据量, 也就是txt文件的行数, 如果此参数大于数据行数, 读取到的内容为不定态
    reg [7:0] memory[0:DATA_NUM-1] ;//申请250000个8位的存储单元
    reg [4:0] n;
    
    initial 
      begin
        $readmemh("../../../../fitFliterTestData3.txt",memory); //fitFliterTestData2.txt中的数字到memory
        for(n=0;n<=15;n=n+1)   //把15个存储单元的数字打印到 Tcl Console
            $display("%h",memory[n]);
            
    //    outputfile = $fopen("file2.txt","w");
    //	for(n=0;n<=7;n=n+1)   
    //		$fwrite(outputfile,"%b\n",filter_out[n]);
    //	$fclose(outputfile);
      end
//<<-- 导入数据  ---------------------------------------------------

//>>--  传递数据至输入 控制仿真结束 ---------------------
//** 传递数据
//** 数据传递结束 10 个时钟周期后 停止仿真
    reg[$clog2(DATA_NUM)-1:0] i=0;    
initial 
  begin

    ad_data = 8'd0;
    #(clk_period * 10)    
    for (i = 0; i < DATA_NUM; i=i+1) begin

        ad_data = memory[i][7:0];
        #(clk_period*2);
    end        

    #(clk_period * 10) $stop;
  end
//<<--  传递数据至 fir_filter 输入 控制仿真结束 -------------------

仿真结果

图中 sys_clk 为系统时钟,50MHz;sys_rst_n 为系统复位信号;ad_data 为输入信号; da_data 为输出信号。可以看出,经过混频信号经过CIC抽取滤波器后,实现了滤波和降采样。

3.4 下载验证

程序编写完成,经vivado编译综合布局布线,生成比特流文件,连接开发板下载,实验验证。本程序中还例化有一个ila,观察采集信号与滤波后信号。

ila探针结果-00:此结果存在有bug,经CIC滤波器输出数据为补码,但是所使用的dac码值使用原码,需要做一下转换

verilog 复制代码
assign da_din = (cic_to_da[7] == 1'b0) ? (cic_to_da + 8'b0111_1111) : (cic_to_da - 8'b0111_1111) ;

修正后的ila结果,可以看出数据正常,并且符合预期。

示波器观察对比:黄色为输入信号,绿色为输出信号。可以看出,此程序实现了滤波和降采样操作。

4. 问题

  1. 如同在matlab仿真小节中看到的结果一样,经过CIC抽取滤波器后的数据出现了预期之外的谐波峰,后面需要讨论;
  1. 在进行单频信号做输入时,低频(低于50kHz)信号严重失真;高频(接近截至频率fc=250kHz)信号幅度衰减严重
  1. 叠加有高频信号时,上述现象会消失,尤其是低频段的现象

上述两种原因怀疑是因为数据长度选择不恰当和滤波器带宽内增益变化大引起,后面需要讨论验证。