作者:桂。
时间:2018-02-06 12:10:14
链接:http://www.cnblogs.com/xingshansi/p/8421001.html
前言
一、基本型实现
首先从最基本的FIR入手:
对应module:
`default_nettype none
//
module smplfir(i_clk, i_ce, i_val, o_val);
parameter IW=15;
localparam OW=IW+1;
input wire i_clk, i_ce;
input wire [(IW-1):0] i_val;
output reg [(OW-1):0] o_val; reg [(IW-1):0] delayed; initial delayed = 0;
always @(posedge i_clk)
if (i_ce)
delayed <= i_val; always @(posedge i_clk)
if (i_ce)
o_val <= i_val + delayed; endmodule
二、通用版FIR
前文里最多涉及阶数为5的FIR,这里给出适用任意阶、给定位宽的FIR。
A-参数转化
vivado仿真用到浮点->定点,需要将给定数据转为定点补码、或通过补码读取数据。
1)浮点转定点补码:
clc;clear all;close all;
%=============产生输入信号==============%
N=12; %数据位宽
load fir128.mat;
y_n = fir128;
y_n=round(y_n*(2^(N-3)-1)); %N比特量化;如果有n个信号相加,则设置(N-n)
%=============设置系统参数==============%
L=length(y_n); %数据长度
%=================画图==================%
stem(1:L,y_n);
%=============写入外部文件==============%
fid=fopen('win.txt','w'); %把数据写入sin_data.txt文件中,如果没有就创建该文件
for k=1:length(y_n)
B_s=dec2bin(y_n(k)+((y_n(k))<0)*2^N,N);
for j=1:N
if B_s(j)=='1'
tb=1;
else
tb=0;
end
fprintf(fid,'%d',tb);
end
fprintf(fid,'\r\n');
end fprintf(fid,';');
fclose(fid);
原型滤波器fir128为128阶的FIR滤波器。
生成的txt调用:$readmemb("*.txt",data);
2)给定补码,读取原数据:
clc;clear all;close all;
filename = 'win.txt';
fid = fopen(filename);
data_cell = textscan(fid,'%s','HeaderLines',0);
data = data_cell{1,1}; Nbit = 12;%number of bits
len = length(data)-1;%length of filter
wins = zeros(1,len);
for i = 1:len
str_win = data{i};
if (str_win(1) == '0')
wins(i) = bin2dec(str_win(2:end));
end
if (str_win(1) == '1')
wins(i) = -bin2dec(num2str(ones(1,Nbit-1)))+bin2dec(str_win(2:end));
end
end
wvtool(wins)
得到滤波器特性如下图所示,当然也可以hex2dec转为16进制,思路一致。
B-仿真模型
testbench:
`timescale 1ns / 1ps
module tb;
// Inputs
reg Clk;
reg rst;
// Outputs
parameter datawidth = 12;
wire signed [2*datawidth-1:0] Yout;
//Generate a clock with 10 ns clock period.
initial Clk <= 0; always #5 Clk = ~Clk; //Initialize and apply the inputs.
//-------------------------------------//
parameter data_num = 32'd1024;
integer i = 0;
reg [datawidth-1:0] Xin[1:data_num];
reg [datawidth-1:0] data_out; initial begin
rst = 1;
#20
rst = 0;
$readmemb("D:/PRJ/vivado/simulation_ding/009_lpf6tap/matlab/sin_data.txt",Xin);
end always @(posedge Clk) begin
if(rst)
begin
data_out <= 0;
end
else
begin
data_out <= Xin[i];
i <= i + 8'd1;
end
end fastfir firinst(
.i_clk(Clk),
.i_reset(rst),
.i_ce(1'b1),
.i_sample(data_out),
.o_result(Yout)
);
endmodule
fast.v:
//
`default_nettype none
//
module fastfir(i_clk, i_reset, i_ce, i_sample, o_result);
parameter NTAPS=127, IW=12, TW=IW, OW=2*IW+7;
input wire i_clk, i_reset;
//
input wire i_ce;
input wire [(IW-1):0] i_sample;
output wire signed [(2*IW-1):0] o_result; reg [(TW-1):0] tap [0:NTAPS];
wire [(TW-1):0] tapout [NTAPS:0];
wire [(IW-1):0] sample [NTAPS:0];
wire [(OW-1):0] result [NTAPS:0];
wire tap_wr;
// The first sample in our sample chain is the sample we are given
assign sample[0] = i_sample;
// Initialize the partial summing accumulator with zero
assign result[0] = 0; //observe filter
reg [IW-1:0] fir_coef;
integer i = 0;
always @(posedge i_clk)
begin
if(i_reset) fir_coef <= 0;
else
begin
fir_coef <= tap[i];
i <= i+ 8'd1;
end
end
genvar k;
generate
begin
initial $readmemb("D:/PRJ/vivado/simulation_ding/009_lpf6tap/matlab/win.txt", tap);
assign tap_wr = 1'b1;
end
for(k=0; k<NTAPS; k=k+1)
begin: FILTER firtap #(.FIXED_TAPS(1'b1),
.IW(IW), .OW(OW), .TW(TW),
.INITIAL_VALUE(0))
tapk(
.i_clk(i_clk),
.i_reset(i_reset),
.i_tap_wr(tap_wr),
.i_tap( tap[k]),
.o_tap(tapout[k+1]),
.i_ce(i_ce),
.i_sample(sample[0]),
.o_sample(sample[k+1]),
.i_partial_acc(result[k]),
.o_acc( result[k+1])
);
end endgenerate assign o_result = result[NTAPS][2*IW-1:0]; endmodule
firtap.v:
//
`default_nettype none
//
module firtap(i_clk, i_reset, i_tap_wr, i_tap, o_tap,
i_ce, i_sample, o_sample,
i_partial_acc, o_acc);
parameter IW=12, TW=IW, OW=IW+TW+8;
parameter [0:0] FIXED_TAPS=1;
parameter [(TW-1):0] INITIAL_VALUE=0;
//
input wire i_clk, i_reset;
//
input wire i_tap_wr;
input wire [(TW-1):0] i_tap;
output wire signed [(TW-1):0] o_tap;
//
input wire i_ce;
input wire signed [(IW-1):0] i_sample;
output reg [(IW-1):0] o_sample;
//
input wire [(OW-1):0] i_partial_acc;
output reg [(OW-1):0] o_acc;
// reg [(IW-1):0] delayed_sample;
reg signed [(TW+IW-1):0] product; // Determine the tap we are using
generate
if (FIXED_TAPS != 0)
// If our taps are fixed, the tap is given by the i_tap
// external input. This allows the parent module to be
// able to use readmemh to set all of the taps in a filter
assign o_tap = i_tap; else begin
// If the taps are adjustable, then use the i_tap_wr signal
// to know when to adjust the tap. In this case, taps are
// strung together through the filter structure--our output
// tap becomes the input tap of the next tap module, and
// i_tap_wr causes all of them to shift forward by one.
reg [(TW-1):0] tap; initial tap = INITIAL_VALUE;
always @(posedge i_clk)
if (i_tap_wr)
tap <= i_tap;
assign o_tap = tap; end endgenerate // Forward the sample on down the line, to be the input sample for the
// next component
always @(posedge i_clk)
if (i_reset)
begin
delayed_sample <= 0;
o_sample <= 0;
end else if (i_ce)
begin
// Note the two sample delay in this forwarding
// structure. This aligns the inputs up so that the
// accumulator structure (below) works.
delayed_sample <= i_sample;
o_sample <= delayed_sample;
end // Multiply the filter tap by the incoming sample
always @(posedge i_clk)
if (i_reset)
product <= 0;
else if (i_ce)
product <= o_tap * i_sample; // Continue summing together the output components of the FIR filter
always @(posedge i_clk)
if (i_reset)
o_acc <= 0;
else if (i_ce)
o_acc <= i_partial_acc
+ { {(OW-(TW+IW)){product[(TW+IW-1)]}},
product }; // Make verilator happy
// verilate lint_on UNUSED
wire unused;
assign unused = i_tap_wr;
// verilate lint_off UNUSED
endmodule
仿真结果: