利用FPGA实现全串行低通FIR滤波器

利用FPGA实现全串行低通FIR滤波器

设计一个15阶(长度为16)的具有线性相位低通FIR滤波器,采用布拉克曼窗函数设计,截止频率为500HZ,抽样频率为2000HZ;采用FPGA实现全串行FIR滤波器,系数的量化位数为12比特,输入数据位数为12比特,输出数据位数为29比特,系统时钟为16KHZ

设计思路:首先采用MATLAB根据要求设计出滤波器系数,并仿真出系数量化前后的幅频响应曲线;根据图4-17所示的结构采用Verilog HDL语言再FPGA中实现该滤波器;采用MATLAB仿真出具有白噪声特性的输入信号,以及由200HZ及800HZ单频信号合成的输入信号;将仿真的输入信号作为Verilog HDL测试输入信号,用MOdelSim仿真滤波器输出信号。采用MATLAB分析滤波器输出信号,比较输入输出信号的时域及频域图。

1.采用MATLAB设计出滤波器系数

function Q12 = My_E4_7_fir8
%设置参数
N = 15; %N阶 长度为N+1 滤波器
fc = 500;   %截止频率为500HZ
fs = 2000;  %抽样频率为2000HZ
wn = fc/(fs/2); %归一化截止频率
ftype = 'low';  %滤波器类型
window = blackman(N+1)';   %布拉克曼窗函数 与滤波器长度一样
B = 12;     %量化位数
%用firl函数设计低通滤波器
b = fir1(N,wn,ftype,window);
Q12 = round(b/max(abs(b))*(2^(B-1)-1));     %12比特量化
%求其幅度响应 
m_blcak = 20*log(abs(fft(b,1024)))/log(10); 
m12_black = 20*log(abs(fft(Q12,1024)))/log(10); 
%保证最大值相同(都为零),好进行比较
m_blcak = m_blcak - max(m_blcak);
m12_black = m12_black - max(m12_black);
%figure(2)
%x_f = [0:fs/length(m_blcak):fs-fs/length(m_blcak)];
%plot(x_f,m_blcak,'-',x_f,m12_black,'--');
%xlabel('频率(Hz)');ylabel('幅度(dB)');
%legend('未量化','12bit量化');
%grid;
%figure(3)
%x_f_2 = [0:fs/length(m_blcak):fs/2];
%plot(x_f_2,m_blcak(1:length(x_f_2)),'-',x_f_2,m12_black(1:length(x_f_2)),'--');
%xlabel('频率(HZ)');ylabel('幅度(dB)');
%legend('未量化','12bit量化');
%grid;

在这里插入图片描述

图1 系数量化前后幅频响应

2.采用MATLAB仿真滤波器测试数据及滤波后的输出数据

  采用MATLAB仿真滤波器测试数据、仿真测试数据经滤波器滤波后的输出数据,以便与FPGA实现后的结果进行比较,并判断FPGA实现的正确性。首先需要仿真生成12比特量化的、抽样频率为2000HZ的高斯噪声,以及两个频率分别为200HZ与800HZ信号的合成信号,并将仿真的数据转化成二进制数据写入文本文件中,供FPGA仿真程序读取;然后,仿真出量化后的数据经滤波器滤波后的输出结果。

%定义参数

fs = 2000;  %抽样频率
f1 = 200;   %信号1频率
f2 = 800;   %信号2频率
N = 12;     %量化位数为12
t = 0:1/fs:1; %数据采样时间长度
%生成信号
s1 = sin(2*pi*f1*t);    %信号1
s2 = sin(2*pi*f2*t);    %信号2
s = s1 + s2;    %合成信号
%产生随机序列信号
noise = randn(1,length(t)); %生成均值为0,方差为1,大小为[1,length(t)]的高斯噪声
%归一化处理
s = s/max(abs(s));
noise = noise/max(abs(noise));
%12比特量化
s_12 = round(s*(2^(N-1)-1));  %-2^11——2^11
noise_12 = round(noise*(2^(N-1)-1));
%调用自己设计的滤波器对信号进行滤波
Q12=My_E4_7_fir8;
Filter_noise=filter(Q12,1,noise_12);  %由于是filter滤波器,所以b=Q12,a=1
Filter_s=filter(Q12,1,s_12);
%求信号的幅频响应
m_s = 20*log(abs(fft(s,1024)))/log(10);    m_s = m_s - max(m_s); 
m_noise = 20*log(abs(fft(noise,1024)))/log(10);    m_noise = m_noise - max(m_noise);
%求滤波后信号的幅频响应
m_filter_s = 20*log(abs(fft(Filter_s,1024)))/log(10);    m_filter_s = m_filter_s - max(m_filter_s); 
m_filte_noise = 20*log(abs(fft(Filter_noise,1024)))/log(10);    m_filte_noise = m_filte_noise - max(m_filte_noise);
%求滤波器的幅频响应
m_Q12 = 20*log(abs(fft(Q12,1024)))/log(10);    m_Q12 = m_Q12 - max(m_Q12); 
%设置幅频响应的横坐标单位为Hz
x_f = 0:fs/length(m_s):fs/2; 
%只显示正频率部分的幅频响应
m_s = m_s(1:length(x_f));
m_Q12 = m_Q12(1:length(x_f));
m_noise = m_noise(1:length(x_f));
m_filter_s = m_filter_s(1:length(x_f));
m_filte_noise = m_filte_noise(1:length(x_f));

%画幅频响应曲线

figure(2)
subplot(211)
plot(x_f,m_s,'-.',x_f,m_filter_s,'-',x_f,m_Q12,'--');
xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真合成单频信号滤波前后的频谱');
legend('输入信号频谱','滤波后的频谱','滤波器频谱');
grid;
subplot(212)
plot(x_f,m_noise,'-.',x_f,m_filte_noise,'-',x_f,m_Q12,'--');
xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真白噪声信号滤波前后的频谱');
legend('输入信号频谱','滤波后的频谱','滤波器频谱');
grid;
%将生成的数据以十进制数据格式写入txt文件中

fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_noise_12.txt','w');
fprintf(fid,'%8d\r\n',noise_12);
fprintf(fid,';'); 
fclose(fid);
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_s_12.txt','w');
fprintf(fid,'%8d\r\n',s_12);
fprintf(fid,';'); 
fclose(fid);
%将生成的数据以二进制数据格式写入txt文件中
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_noise_12.txt','w');
for i=1:length(noise_12)
​    B_noise=dec2bin(noise_12(i)+(noise_12(i)<0)*2^N,N);   %补码形式for j=1:N
​       if B_noise(j)=='1'
​           tb=1;else
​           tb=0;endfprintf(fid,'%d',tb);endfprintf(fid,'\r\n');
end
fprintf(fid,';'); 
fclose(fid);
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_s_12.txt','w');
for i=1:length(s_12)
​    B_s=dec2bin(s_12(i)+(s_12(i)<0)*2^N,N)for j=1:N
​       if B_s(j)=='1'
​          tb=1;else
​           tb=0;endfprintf(fid,'%d',tb);endfprintf(fid,'\r\n');
end
fprintf(fid,';'); 
fclose(fid);

在这里插入图片描述

图2 MATLAB仿真的信号滤波前后的频谱图

  不难发现,800HZ频率的信号几乎被滤掉了。

3.采用Verilog HDL语言设计全串行低通FIR滤波器

`

module My_FirlFullSerial(
//滤波器系数为12位量化 输入数据也是12位 且都是有符号数 都是以补码形式输入(计算机中数都是以补码形式存在并进行加减)
//使用到的是15阶FIR滤波器 长度为N=16 
//由FIR滤波器计算公式  最终输出的数据位宽 > (16*2^12)*2^12 = 2^(4+12+12) = 2^28 
//所以滤波后的数据保险起见可以为29位

	input clk	,		//时钟信号 时钟频率为16KHZ
	input rst	,		//复位信号
	input  signed[11:0]	Xin,	//输入数据 数据输入频率为2KHZ
	output signed[28:0] Yout	//滤波后的输出数据 
);

//实例化有符号数乘法器IP核 mult
reg signed [11:0] coe;	//滤波器系数
wire signed [12:0] add_s;	//输入数据中 对称数据的和 第n个和最后N-n个
wire signed [24:0] Mout;	//两项乘积数的和

mult Umult(      //乘法器IP核
	.clock(clk),	//时钟信号
	.dataa(coe),	
	.datab(add_s),
	.result(Mout)
);

//实例化有符号数加法器IP核,对输入数据进行1比特符号位扩展,输出结果为13比特数据
//由于结果为13比特 ,所以把输入数据都扩展为了13位
reg signed [12:0] add_a;
reg signed [12:0] add_b;
adder Uadder(	//加法器IP核 
	.dataa(add_a),
	.datab(add_b),
	.result(add_s)
);
//时钟计数器 每计数八次为一次循环
reg [2:0] count;
always @(posedge clk or negedge rst)
	if(!rst)
		count <= 3'd0;
	else count <= count + 1'd1;

//给每一个数据寄存器赋值 共16个
reg [11:0] Xin_reg[15:0];	//输入数据寄存器 
reg [3:0] i,j;	
always @(posedge clk or negedge rst)
	if(!rst) begin    //复位时每个寄存器都清零
		for(i=0;i<15;i=i+1)
			Xin_reg[i] <= 12'd0;
	end
	else if(count == 3'd7) begin	//当一次循环结束时 开始加载下一个数据 并所有数据右移
			for(j=0;j<15;j=j+1)
				Xin_reg[j+1] <= Xin_reg[j];
			Xin_reg[0] <= Xin;
		end
	else ;
	
//将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
//需要注意的是,下面程序只用了了一个加法器及一个乘法器
//以8倍率的速率调用乘法器IP核,由于滤波器长度为16比特,系数具有一定的对称性
//故可在一个数据周期内完成所有8个滤波器系数与数据的乘法运算
//为了保证加法运算不溢出,输入、输出数据均扩展为13比特
always @(posedge clk or negedge rst)
	if(!rst) begin
		coe <= 12'd0;
		add_a <= 13'd0;
		add_b <= 13'd0;
	end
	else if(count == 3'd0) begin
		coe <= 12'd0;	//第一个滤波器及最后一个滤波器系数的值
		add_a <= {Xin_reg[0][11],Xin_reg[0]}; 	//第一个滤波器系数对应的输入值 并将12位的输入数据扩展为13位
		add_b <= {Xin_reg[15][11],Xin_reg[15]};	//若最高位为1 则补1 最高位为零 则补零 总之就是补高位的值
	    end
	else if(count == 3'd1) begin
		coe <= 12'hffd;	//c1
		add_a <= {Xin_reg[1][11],Xin_reg[1]}; 	
		add_b <= {Xin_reg[14][11],Xin_reg[14]};	
	    end
	else if(count == 3'd2) begin
		coe <= 12'h00f;	
		add_a <= {Xin_reg[2][11],Xin_reg[2]}; 	
		add_b <= {Xin_reg[13][11],Xin_reg[13]};	
	    end
	else if(count == 3'd3) begin
		coe <= 12'h02e;	
		add_a <= {Xin_reg[3][11],Xin_reg[3]}; 	
		add_b <= {Xin_reg[12][11],Xin_reg[12]};	
	    end
	else if(count == 3'd4) begin
		coe <= 12'hf8b;	
		add_a <= {Xin_reg[4][11],Xin_reg[4]}; 	
		add_b <= {Xin_reg[11][11],Xin_reg[11]};	
	    end
	else if(count == 3'd5) begin
		coe <= 12'hef9;	
		add_a <= {Xin_reg[5][11],Xin_reg[5]}; 	
		add_b <= {Xin_reg[10][11],Xin_reg[10]};	
	    end
	else if(count == 3'd6) begin
		coe <= 12'h24e;	
		add_a <= {Xin_reg[6][11],Xin_reg[6]}; 	
		add_b <= {Xin_reg[9][11],Xin_reg[9]};	
	    end
	else begin
		coe <= 12'h7ff;	
		add_a <= {Xin_reg[7][11],Xin_reg[7]}; 	
		add_b <= {Xin_reg[8][11],Xin_reg[8]};	
	    end
		
//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
//考虑到乘法器及累加器的延时,需要在计数器count=2时对累计器清零,同时输出滤波器结果
//类似的延时长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看
reg signed [28:0] sum;
reg signed [28:0] yout;
always @(posedge clk or negedge rst)
	if(!rst) begin 
		sum <= 29'd0;
		yout <= 29'd0;
	end
	else if(count == 2 ) begin  //计数到2的时候 说明一次循环结束
			yout <= sum;	//将累加值保存到yout变量中
			sum = 29'd0;	//累加值重新清零	
			sum = sum + Mout;
		end
	else sum = sum + Mout;

assign Yout = yout;	
	
endmodule

4.编写Verilog HDL测试激励文件

`timescale 1 ns/ 1 ns //设置仿真时间单位:ns
module FirFullSerial_vlg_tst();
// constants                                           
// general purpose registers
reg eachvec;
// test vector input registers
reg [11:0] Xin;
reg clk;
reg rst;
wire clk_data; //数据时钟,速率为系统时钟clk的1/8;
// wires                                               
wire [28:0]  Yout;

// assign statements (if any)                          
FirFullSerial i1 (
// port map - connection between master ports and signals/registers   
	.Xin(Xin),
	.Yout(Yout),
	.clk(clk),
	.rst(rst)
);

parameter clk_period=62500; //设置时钟信号周期(频率):16kHz
parameter clk_period_data=clk_period*8;
parameter clk_half_period=clk_period/2;
parameter clk_half_period_data=clk_half_period*8;
parameter data_num=2000;  //仿真数据长度
parameter time_sim=data_num*clk_period; //仿真时间

initial
begin
	//设置时钟信号初值
	clk=1;
	//clk_data=1;
	//设置复位信号
	rst=1;
	#200000 rst=0;
	//设置仿真时间
	#time_sim $finish;
	//设置输入信号初值
	Xin=12'd10;
end

//产生时钟信号
always                                                 
	#clk_half_period clk=~clk;

reg [2:0] cn_clk=3'd0;
always @(posedge clk) cn_clk <= cn_clk + 3'd1;
assign clk_data = cn_clk[2];

//从外部TX文件(SinIn.txt)读入数据作为测试激励
integer Pattern;
reg [11:0] stimulus[1:data_num];
initial
	begin
		//文件必须放置在"工程目录\simulation\modelsim"路径下
		//readmemb("E4_7_Bin_noise.txt",stimulus);
		$readmemb("E4_7_Bin_s.txt",stimulus);
		Pattern=0;
		repeat(data_num)
			begin 
				@(posedge clk_data);
				Pattern=Pattern+1;
				Xin=stimulus[Pattern];
			end
	end

//将仿真数据dout写入外部TXT文件中(out.txt)
integer file_out;
initial 
begin
   //文件放置在"工程目录\simulation\modelsim"路径下                                                  
	//file_out = fopen("E4_7_Noiseout.txt");
	file_out = fopen("E4_7_Sout.txt");
	if(!file_out)
		begin
			display("could not open file!");
			finish;
		end
end
wire rst_write;
wire signed [28:0] dout_s;
assign dout_s = Yout;                //将dout转换成有符号数据
assign rst_write = clk_data & (!rst);//产生写入时钟信号,复位状态时不写入数据
always @(posedge rst_write )
	$fdisplay(file_out,"%d",dout_s);
endmodule

5.利用Modelsim对FPGA程序进行功能和时序仿真

6. 编写MATLAB程序,分析FPGA中输出数据是否与MATLAB仿真中相同

7.总结

  看完书本这部分内容,几乎都自己重新写了一遍,算是大概了解了这个流程。虽然由于自身技术原因,利用FPGA仿真那部分操作出了点问题,现在还不怎么明白,所以5、6节我没加上去内容。总体来说还可以

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用CCS实现低通FIR滤波器的步骤: 1. 打开CCS软件,在Project Explorer中右键单击并选择New -> CCS Project。 2. 在弹出的对话框中输入项目名称,选择适当的设备类型和编译器版本,然后单击Finish。 3. 在Project Explorer中右键单击项目名称,选择New -> Source File,在弹出的对话框中输入文件名,并选择C Source File,然后单击Finish。 4. 在新建的源文件中,编写低通FIR滤波器的代码。以下是一个简单的例子: ``` #define N 8 // 滤波器的阶数 float h[N] = {0.1, 0.2, 0.3, 0.2, 0.1, 0.05, 0.02, 0.01}; // 滤波器系数 float filter(float x) { static float history[N] = {0}; // 存储历史输入值 float y = 0; // 滤波器的输出值 // 将新的输入值插入历史输入值数组中 for (int i = N-1; i >= 1; i--) { history[i] = history[i-1]; } history[0] = x; // 计算滤波器的输出值 for (int i = 0; i < N; i++) { y += h[i] * history[i]; } return y; } ``` 5. 在Project Explorer中右键单击项目名称,选择New -> Target Configuration File,在弹出的对话框中输入文件名,并选择合适的设备类型和调试器,然后单击Finish。 6. 在Project Explorer中右键单击项目名称,选择Build Project,编译代码并生成可执行文件。 7. 在Project Explorer中右键单击项目名称,选择Debug As -> CCS Debug,启动调试器并加载可执行文件。 8. 在调试器中设置断点并运行程序,验证低通FIR滤波器的输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值