LMS自适应滤波器的FPGA实现

LMS自适应滤波器的FPGA实现

简介

数字滤波器简介

​ 滤波器一直以来都是信号处理的重要工具,在通信、医学和图像处理等领域也有着至关重要的作用。随着电子计算机、大规模集成电路及芯片工业的发展,又促使数字滤波器能通过软件计算机模拟实现,再通过大规模集成电路及芯片设计达到硬件实现。数字滤波器通常指的是通过对数字信号进行数学运算和逻辑处理,合理改变滤波参数,完成滤波功能的装置。随着芯片工业及数字信号处理技术的发展,数字滤波器的优势愈发明显。
​ 数字滤波器相较于模拟滤波器,在应对系统外部环境时(如温度、外部信号干扰等),滤波器会因为器件本身的特性差异而产生不同的滤波效果,而数字滤波器具有统一性,只要程序实现的功能相同,产生的滤波效果基本不会有差异;并且数字滤波器还具有高度的灵活性和扩展性,只要改变程序代码,即可获得需要的滤波性能,方便快捷;随着近些年来数字集成电路的高速发展,芯片的性能和价格优势越来越明显,数字滤波器有着优秀的发展潜力。

​ 数字滤波器的主流结构设计方法有两种,按照冲击响应的方式可分为无限冲击响应(IIR)滤波器和有限冲击响应(FIR)滤波器。两种滤波方式都有其各自优势,但在面对线性相位结构系统上,FIR 滤波器结构有着得天独厚的优势。

自适应滤波器简介

​ 在很多信号处理系统中,并没有信号的先验统计特性,不能使用某一固定参数的滤波器来处理,比如信道均衡、回声消除以及其他因素之间的系统模型等,均采用了调整系数的滤波器,称为自适应滤波器。这样的滤波器结合了允许滤波器系数适应于信号统计特性的算法。

  • 自适应滤波器的特点
  1. 没有关于待提取信息的先验统计知识
  2. 直接利用观测数据依据某种判据在观测过程中不断递归更新
  3. 最优化
  • 自适应滤波器分类
  1. 按结构分:横向结构、格型结构
  2. 按算法分:随机梯度、最小二乘
  3. 按处理方式分:成批处理、递归处理
  • 自适应滤波器应用
  1. 噪声抵消
  2. 回音消除
  3. 谱线增强
  4. 通道均衡
  5. 系统辨识

LMS算法简介

​ 最小均方误差算法是B.Widrow和Hoff于 1960 年提出的。由于实现简单且对信号的统计特性变化具有稳健性,LMS算法获得了极为广泛的应用。LMS算法是基于最小均方误差准则(MMSE)的维纳滤波器和最陡下降法提出的。LMS算法使用随机梯度下降的方法实现代价函数最小化,具体地说,每次迭代时权矢量沿着误差性能曲面的梯度估值的负方向按一定比例更新。误差输出
ϵ k \epsilon_k ϵk
可写为:
ϵ k = d k − X k T ∗ W k \epsilon_k = d_k-X_k^T*W_k ϵk=dkXkTWk

理论基础

FIR滤波器

​ 单位脉冲响应长度是有限的滤波器就是 FIR 滤波器。单位取样 h(n)是一个 N 点长的有限长序列,0≤n≤N-1。滤波器的输出 y(n)可表示为输入序列 x(n)与单位取样响应 h(n)的线性卷积。所以该关系表达式为:
y ( n ) = ∑ k = 0 N − 1 x ( n − k ) h ( k ) = x ( n ) ∗ h ( n ) y(n) = \sum_{k=0}^{N-1}x(n-k)h(k) = x(n)*h(n) y(n)=k=0N1x(nk)h(k)=x(n)h(n)
系统函数为
H ( z ) = ∑ n = 0 N − 1 h ( n ) z − n = h ( 0 ) + h ( 1 ) z − 1 + h ( 2 ) z − 2 + . . . + h ( N − 1 ) z − ( N − 1 ) H(z) = \sum_{n=0}^{N-1}h(n)z^{-n}\\=h(0)+h(1)z^{-1}+h(2)z^{-2}+...+h(N-1)z^{-(N-1)} H(z)=n=0N1h(n)zn=h(0)+h(1)z1+h(2)z2+...+h(N1)z(N1)
根据系统函数,该函数只有原点上存在极点,系统不存在极点,所以可知道 FIR 滤波系统有着绝对的全局稳定性。FIR 滤波器是由一个抽头延迟线加法器和乘法器集合构成的,每一个乘法器的操作系数就是一个 FIR 滤波器。FIR 滤波器具有严格的线性相位特性,但是当 FIR 滤波器单位取样响应为偶对称的相位特性,和单位取样函数为奇对称的相位特性是不完全相同的。设单位响应不为零的点有 M+1 个,不管是奇对称还是偶对称,系统都具有 M/2 个延迟,但是奇对称会多产生 90°的移相,下图2.1是 FIR 滤波器的线性相位特性:

LMS自适应滤波器的FPGA实现_第1张图片

接下来,介绍 FIR 滤波器的幅度特性,根据奇偶数和奇偶对称再将 FIR 滤波器分为四种情况。在图 2.2 中,a 为偶数的偶对称,b 为奇数的偶对称,c 为偶数的奇对称,d为偶数的奇对称:

LMS自适应滤波器的FPGA实现_第2张图片

LMS自适应滤波器的FPGA实现_第3张图片

如果要想进行 FPGA 实现,必须要结合 FPGA 本身的特点来使用合适的 FIR 滤波器结构,所以得到四种合适的 FPGA 设计结构:

第一种,直接行 FIR 滤波器结构,根据图 2.3 可知道整个运算需要 M+1 个乘法,M个延时和 1 个加法器,对于 FPGA 器件来说,乘法是很消耗资源的。所以一般要对该结构进行对称改进得到对称直接型结构。

LMS自适应滤波器的FPGA实现_第4张图片

第二种是级联型结构,因为 FIR 滤波器系统结构并没有极点,只有零点。由此得到如下公式:
H ( z ) = ∑ n = 0 N − 1 h ( n ) z − n = ∏ k = 1 N c ( b 0 k + b 1 k z − 1 + b 2 k z − 2 ) H(z) = \sum_{n=0}^{N-1}h(n)z^{-n}=\prod_{k=1}^{N_c}(b_{0k}+b_{1k}z^{-1}+b_{2k}z^{-2}) H(z)=n=0N1h(n)zn=k=1Nc(b0k+b1kz1+b2kz2)
得到结构:

LMS自适应滤波器的FPGA实现_第5张图片

可以知道需要大约 3[Nc]的乘法结构,需要乘法实在过多不易于 FPGA 实现,并且结构不易于改进,所以包括文中在内大量学者都不采用这种结构。

第三种是频率取样型结构,该结构是 FIR 的系统函数在单位元上做 N 等分处理,然后取样,相当于单位取样响应 h(n)的离散傅里叶变换 H(K)。H(K)和系统函数 H(z):
H ( z ) = ( 1 N ) ( 1 − r N z − N ) ∑ k = 0 N − 1 H ( k ) 1 − r W N − K z − 1 H(z) = (\frac{1}{N})(1-r^Nz^{-N})\sum_{k=0}^{N-1}\frac{H(k)}{1-rW_N^{-K}z^{-1}} H(z)=(N1)(1rNzN)k=0N11rWNKz1H(k)
由此结构为:

LMS自适应滤波器的FPGA实现_第6张图片

该结构有很多不适于文中实现的缺点。首先该结构所乘系数为复数,运算量急剧增加;然后是所有谐振器都在单位圆上,当系数变化时,极点会随之而变化,引发系统振荡;最后是对于文中后续实现的滤波器模块,该结构灵活性很差,不易于改进和实现。

第四种是快速卷积型结构,由于 FIR 滤波器单位取样响应是一个 N 点长的有限长序列,0≤n≤N-1,输入序列 x(n)和单位取样响应 h(n)线性卷积即为输出 y(n),所以得到系统输出函数:
y ( n ) = ∑ k = 0 N − 1 x ( k ) h ( n − k ) = x ( n ) ∗ h ( n ) y(n) = \sum_{k=0}^{N-1}x(k)h(n-k) = x(n)*h(n) y(n)=k=0N1x(k)h(nk)=x(n)h(n)
取适当卷积 L 点数(L≥N+M-1,M-1≥n,M 点为输入序列 x(n)的长,L 点循环可以表示线性卷积),根据卷积的性质的到结构:

LMS自适应滤波器的FPGA实现_第7张图片

当 N 和 M 很大时,IDFT 和 DFT 都是快速傅里叶计算,计算线性卷积的速度会很快。对于 FIR 滤波器设计方法上,文中直接采用 MATLAB 软件进行 FIR 滤波器单位取样响应的设计方法。

LMS算法

自适应滤波技术即是指能够根据具体环境和噪声特征随时修改滤波参数,使得滤波器可以随时改变以应对输入信号,得出有用信号,得到最好的滤波效果。评判一个自适应滤波系统的好坏主要由:收敛速度,计算复杂度,稳态性等几个方面来衡量。

LMS自适应滤波器的FPGA实现_第8张图片

自适应滤波器的基本结构,如图 2.9 所示。n 时刻,输入信号 x(n)通过 n-1 时刻调整后的滤波器得出输出信号 y(n),输出信号 y(n)和期望信号 d(n)比对得出误差信号 e(n),进而调整 n+1 时刻滤波器的滤波参数,整个过程让输出信号更加趋向于理想信号。

LMS自适应滤波器的FPGA实现_第9张图片

根据图 2.10 横向滤波器结构,可以得到:
X ( n ) = [ x ( n ) , x ( n − 1 ) , . . . x ( n − M + 1 ) ] T W ( n ) = [ w ( n ) , w ( n − 1 ) , . . . w ( n − M + 1 ) ] T e ( n ) = d ( n ) − y ( n ) = d ( n ) − W T ( n ) X ( n ) X(n) = [x(n),x(n-1),...x(n-M+1)]^T\\W(n) = [w(n),w(n-1),...w(n-M+1)]^T\\e(n) = d(n)-y(n)=d(n)-W^T(n)X(n) X(n)=[x(n),x(n1),...x(nM+1)]TW(n)=[w(n),w(n1),...w(nM+1)]Te(n)=d(n)y(n)=d(n)WT(n)X(n)
经典的 LMS 自适应滤波算法为:
y ( n ) = W H ( n ) X ( n ) e ( n ) = d ( n ) − y ( n ) W ( n + 1 ) = W ( n ) + 2 μ X ( n ) e ( n ) y(n) = W^H(n)X(n)\\e(n) = d(n)-y(n)\\W(n+1) = W(n)+2\mu X(n)e(n) y(n)=WH(n)X(n)e(n)=d(n)y(n)W(n+1)=W(n)+2μX(n)e(n)
其中,X(n)为 n 时刻输入信号向量,W(n)为 n 时刻自适应滤波器的抽头权向量,μ 这里定义为步长因子。由经典的 LMS 自适应滤波算法公式可知,权向量W(n)为更新的输入向量 X(n)数据,即为滤波器参数。由于相继输入数据 X(n)和权向量W(n)是相互独立的。所以可知 W(n)权向量的数学期望是:
E [ W ( n + 1 ) ] = E [ W ( n ) ] + 2 μ ∗ E [ e ( n ) X ( n ) ] = E [ W ( n ) ] − 2 μ R { E [ W ( n ) ] − W 0 } E[W(n+1)]\\ = E[W(n)]+2\mu*E[e(n)X(n)]\\= E[W(n)]-2\mu R\lbrace E[W(n)]-W_0\rbrace E[W(n+1)]=E[W(n)]+2μE[e(n)X(n)]=E[W(n)]2μR{E[W(n)]W0}

W 0 为 权 相 量 W ( n ) 的 最 佳 维 纳 解 , E [ X ( n ) X T ( n ) ] 为 输 入 向 量 X ( n ) 的 自 相 关 矩 阵 。 W_0为权相量 W(n)的最佳维纳解,E[X(n)X^T(n)]为输入向量 X(n)的自相关矩阵。 W0W(n)E[X(n)XT(n)]X(n)

当步长因子必须满足
0 ≤ μ < 1 λ m a x 0\leq\mu<\frac{1}{\lambda_{max}} 0μ<λmax1
时,自适应算法即可收敛。
λ m a x \lambda_{max} λmax
为其自相关矩阵的最大特征值。随着迭代次数的进行,权向量 W(n)趋近于最佳的维纳解。

变步长LMS

​ 收敛速度快慢,稳态性,计算复杂度低,易于硬件实现都是决定自适应滤波系统优劣的主要因素。但是对于经典的固定步长的 LMS 算法来说,其收敛速度和稳态性之间有着不可调和的矛盾,若其选用较大的步长因子 μ,收敛速度是会加快,但是收敛后的误差信号不稳定,也就是稳态性不好;同理若是选用较小步长因子 μ,收敛速度会变慢,但是收敛后的稳态性较好。所以针对这种矛盾性,很多学者引入函数模型,使得步长因子 μ 和误差 e 之间达成某种关系,从而调和这种矛盾。即:在算法运行的开始阶段,采用较大的步长因子 μ 用来加快收敛速度,而算法误差越来越小,使用较小的步长因子 μ来保证算法稳态性。

基于 Sigmoid 函数变步长最小均方算法,其步长因子 μ(n)是误差 e(n)的 Sigmoid 函数:
μ ( n ) = β 1 1 + e − α ∣ e ( n ) ∣ − 0.5 \mu(n) = \beta\frac{1}{1+e^{-\alpha|e(n)|}}-0.5 μ(n)=β1+eαe(n)10.5
​ α 控制 Sigmoid 函数的形状,β 决定曲线上升的快慢,控制该函数模型的取值范围。μ(n)随着 e(n)的减小而减小,当 e(n)逐渐接近零时,μ(n)也趋近为零。随着算法的不断收敛,步长的改变也迅速。误差越大,需要加快收敛速度,所以就需要采用越大的步长来加速收敛速度;同理,误差越小,需求系统的稳态性,所以就需要采用较小的误差来确保系统稳态性。

​ 这种改进方法表明变步长因子和误差的关系准则需要满足:当误差比较大时,采用较大的步长,用来加快收敛速度;当误差比较小时,采用较小的步长,用来保证系统的稳态性,从而提高整个算法性能。这样会使自适应滤波算法在开始阶段有较快的收敛速度,达到收敛后又不会有太差的稳定性。

LMS自适应滤波器的FPGA工程实现

FIR滤波器模块

module fir(
    input                clk_i,
    input                rst_n_i,
    input  signed [15:0] data_in,                       //fir_i,

    input signed [15:0] coef1,      //权值
    input signed [15:0] coef2,
    input signed [15:0] coef3,
    input signed [15:0] coef4,
    input signed [15:0] coef5,
    input signed [15:0] coef6,
    input signed [15:0] coef7,
    input signed [15:0] coef8,
    input signed [15:0] coef9,

    output signed [15:0] data_o                   //fir_o        //输出位宽待设置
    );


//移位寄存器
reg signed [15:0] data_shift1;
reg signed [15:0] data_shift2;
reg signed [15:0] data_shift3;
reg signed [15:0] data_shift4;
reg signed [15:0] data_shift5;
reg signed [15:0] data_shift6;
reg signed [15:0] data_shift7;
reg signed [15:0] data_shift8;
reg signed [15:0] data_shift9;

always @(posedge clk_i or negedge rst_n_i) begin
    if(!rst_n_i)begin
        data_shift1 <= 16'd0;
        data_shift2 <= 16'd0;
        data_shift3 <= 16'd0;
        data_shift4 <= 16'd0;
        data_shift5 <= 16'd0;
        data_shift6 <= 16'd0;
        data_shift7 <= 16'd0;
        data_shift8 <= 16'd0;
        data_shift9 <= 16'd0;
    end
    else begin
        data_shift9 <= data_shift8;
        data_shift8 <= data_shift7;
        data_shift7 <= data_shift6;
        data_shift6 <= data_shift5;
        data_shift5 <= data_shift4;
        data_shift4 <= data_shift3;
        data_shift3 <= data_shift2;
        data_shift2 <= data_shift1;
        data_shift1 <= data_in    ;
    end
end


//乘法器

//单独fir时固定的权值数据
//parameter signed COEF1 = 16'h2FB1;           //14'h304F;
//parameter signed COEF2 = 16'h2E51;           //14'h31AF;
//parameter signed COEF3 = 16'h19DC;           //14'h19DC;
//parameter signed COEF4 = 16'h4926;           //14'h4926;
//parameter signed COEF5 = 16'h6927;           //14'h6927;
//parameter signed COEF6 = 16'h4926;           //14'h4926;
//parameter signed COEF7 = 16'h19DC;           //14'h19DC;
//parameter signed COEF8 = 16'h2E51;           //14'h31AF;
//parameter signed COEF9 = 16'h2FB1;           //14'h304F;



reg signed [31:0] multi_data1;
reg signed [31:0] multi_data2;
reg signed [31:0] multi_data3;
reg signed [31:0] multi_data4;
reg signed [31:0] multi_data5;
reg signed [31:0] multi_data6;
reg signed [31:0] multi_data7;
reg signed [31:0] multi_data8;
reg signed [31:0] multi_data9;

always @(posedge clk_i or negedge rst_n_i) begin
    if(!rst_n_i)begin
        multi_data1 <= 32'd0;
        multi_data2 <= 32'd0;
        multi_data3 <= 32'd0;
        multi_data4 <= 32'd0;
        multi_data5 <= 32'd0;
        multi_data6 <= 32'd0;
        multi_data7 <= 32'd0;
        multi_data8 <= 32'd0;
        multi_data9 <= 32'd0;
    end
    else begin
        multi_data1 <= data_shift1 * coef1 ;
        multi_data2 <= data_shift2 * coef2 ;
        multi_data3 <= data_shift3 * coef3 ;
        multi_data4 <= data_shift4 * coef4 ;
        multi_data5 <= data_shift5 * coef5 ;
        multi_data6 <= data_shift6 * coef6 ;
        multi_data7 <= data_shift7 * coef7 ;
        multi_data8 <= data_shift8 * coef8 ;
        multi_data9 <= data_shift9 * coef9 ;
    end
end



//加法树
reg signed [40:0] adder_data_one_1;
reg signed [40:0] adder_data_one_2;
reg signed [40:0] adder_data_one_3;
reg signed [40:0] adder_data_one_4;
reg signed [40:0] adder_data_one_5;

reg signed [40:0] adder_data_two_1;
reg signed [40:0] adder_data_two_2;
reg signed [40:0] adder_data_two_3;

reg signed [40:0] adder_data_three_1;
reg signed [40:0] adder_data_three_2;

reg signed [40:0] adder_data_four;


always @(posedge clk_i or negedge rst_n_i) begin
    if(!rst_n_i)begin
        adder_data_one_1   <= 40'd0;
        adder_data_one_2   <= 40'd0;
        adder_data_one_3   <= 40'd0;
        adder_data_one_4   <= 40'd0;
        adder_data_one_5   <= 40'd0;

        adder_data_two_1   <= 40'd0;
        adder_data_two_2   <= 40'd0;
        adder_data_two_3   <= 40'd0;

        adder_data_three_1 <= 40'd0;
        adder_data_three_2 <= 40'd0;

        adder_data_four    <= 40'd0;
    end
    else begin
        adder_data_one_1   <= multi_data1 + multi_data2;
        adder_data_one_2   <= multi_data3 + multi_data4;
        adder_data_one_3   <= multi_data5 + multi_data6;
        adder_data_one_4   <= multi_data7 + multi_data8;
        adder_data_one_5   <= multi_data9;

        adder_data_two_1   <= adder_data_one_1 + adder_data_one_2;
        adder_data_two_2   <= adder_data_one_3 + adder_data_one_4;
        adder_data_two_3   <= adder_data_one_5;

        adder_data_three_1 <= adder_data_two_1 + adder_data_two_2;
        adder_data_three_2 <= adder_data_two_3;

        adder_data_four    <= adder_data_three_1 + adder_data_three_2;
    end
end


assign data_o = adder_data_four>>24;
//assign fir_o = adder_data_four>>n;

endmodule

误差计算模块

module error_calcu(
    input                clk_i,
    input                rst_n_i,
    input  signed [15:0] data_in,           //滤波完成数据
    input  signed [15:0] data_ref,          //参考数据
    output signed [15:0] error_o            //误差输出
    );

    wire  signed [15:0] error;
    //reg signed [15:0] error_o;

//延时寄存参考数据
    reg signed [15:0] data_shift1;
    reg signed [15:0] data_shift2;
    reg signed [15:0] data_shift3;
    reg signed [15:0] data_shift4;
    reg signed [15:0] data_shift5;
    reg signed [15:0] data_shift6;
    reg signed [15:0] data_shift7;
    reg signed [15:0] data_shift8;
    reg signed [15:0] data_shift9;
    
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i)begin
            data_shift1 <= 16'd0;
            data_shift2 <= 16'd0;
            data_shift3 <= 16'd0;
            data_shift4 <= 16'd0;
            data_shift5 <= 16'd0;
            data_shift6 <= 16'd0;
            data_shift7 <= 16'd0;
            data_shift8 <= 16'd0;
            data_shift9 <= 16'd0;
        end
        else begin
            data_shift1 <= data_ref   ;
            data_shift2 <= data_shift1;
            data_shift3 <= data_shift2;
            data_shift4 <= data_shift3;
            data_shift5 <= data_shift4;
            data_shift6 <= data_shift5;
            data_shift7 <= data_shift6;
            data_shift8 <= data_shift7;
            data_shift9 <= data_shift8;
        end
    end

assign error = data_shift9 - data_in;
assign error_o = error;
//ssign error_o = data_shift9 - data_in;

endmodule

系数更新模块

module coef_update(                     //系数更新模块
    input               clk_i,
    input               rst_n_i,
    input signed [15:0] error_o,          //误差
    input signed [15:0] data_in,        //待滤波数据

    //权值更新
    output signed [15:0] coef1,
    output signed [15:0] coef2,
    output signed [15:0] coef3,
    output signed [15:0] coef4,
    output signed [15:0] coef5,
    output signed [15:0] coef6,
    output signed [15:0] coef7,
    output signed [15:0] coef8,
    output signed [15:0] coef9
    );


    reg signed [15:0] coef1;
    reg signed [15:0] coef2;
    reg signed [15:0] coef3;
    reg signed [15:0] coef4;
    reg signed [15:0] coef5;
    reg signed [15:0] coef6;
    reg signed [15:0] coef7;
    reg signed [15:0] coef8;
    reg signed [15:0] coef9;

    reg signed [15:0] mu;           //遗忘因子(步长)

//延时寄存输入数据
    reg signed [15:0] data_shift1;
    reg signed [15:0] data_shift2;
    reg signed [15:0] data_shift3;
    reg signed [15:0] data_shift4;
    reg signed [15:0] data_shift5;
    reg signed [15:0] data_shift6;
    reg signed [15:0] data_shift7;
    reg signed [15:0] data_shift8;
    reg signed [15:0] data_shift9;
    
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i)begin
            data_shift1 <= 16'd0;
            data_shift2 <= 16'd0;
            data_shift3 <= 16'd0;
            data_shift4 <= 16'd0;
            data_shift5 <= 16'd0;
            data_shift6 <= 16'd0;
            data_shift7 <= 16'd0;
            data_shift8 <= 16'd0;
            data_shift9 <= 16'd0;
            mu          <= 16'd383;             //步长不知道是多少,随便写了一个;
        end
        else begin
            data_shift1 <= data_in   ;
            data_shift2 <= data_shift1;
            data_shift3 <= data_shift2;
            data_shift4 <= data_shift3;
            data_shift5 <= data_shift4;
            data_shift6 <= data_shift5;
            data_shift7 <= data_shift6;
            data_shift8 <= data_shift7;
            data_shift9 <= data_shift8;
        end
    end    


    
//权值更新

    //mu*error*data_in*2
    reg signed [47:0] coef1_reg;
    reg signed [47:0] coef2_reg;
    reg signed [47:0] coef3_reg;
    reg signed [47:0] coef4_reg;
    reg signed [47:0] coef5_reg;
    reg signed [47:0] coef6_reg;
    reg signed [47:0] coef7_reg;
    reg signed [47:0] coef8_reg;
    reg signed [47:0] coef9_reg;
always @(posedge clk_i or negedge rst_n_i) begin
    if(!rst_n_i)begin
        coef1 <= 16'd0;
        coef2 <= 16'd0;
        coef3 <= 16'd0;
        coef4 <= 16'd0;
        coef5 <= 16'd0;
        coef6 <= 16'd0;
        coef7 <= 16'd0;
        coef8 <= 16'd0;
        coef9 <= 16'd0;

        coef1_reg <= 16'd0;
        coef2_reg <= 16'd0;
        coef3_reg <= 16'd0;
        coef4_reg <= 16'd0;
        coef5_reg <= 16'd0;
        coef6_reg <= 16'd0;
        coef7_reg <= 16'd0;
        coef8_reg <= 16'd0;
        coef9_reg <= 16'd0;
    end
    else begin
        coef1_reg <= (mu * error_o * data_shift1)>>2;
        coef2_reg <= (mu * error_o * data_shift2)>>2;
        coef3_reg <= (mu * error_o * data_shift3)>>2;
        coef4_reg <= (mu * error_o * data_shift4)>>2;
        coef5_reg <= (mu * error_o * data_shift5)>>2;
        coef6_reg <= (mu * error_o * data_shift6)>>2;
        coef7_reg <= (mu * error_o * data_shift7)>>2;
        coef8_reg <= (mu * error_o * data_shift8)>>2;
        coef9_reg <= (mu * error_o * data_shift9)>>2;

        coef1 <= coef1 + coef1_reg;
        coef2 <= coef2 + coef2_reg;
        coef3 <= coef3 + coef3_reg;
        coef4 <= coef4 + coef4_reg;
        coef5 <= coef5 + coef5_reg;
        coef6 <= coef6 + coef6_reg;
        coef7 <= coef7 + coef7_reg;
        coef8 <= coef8 + coef8_reg;
        coef9 <= coef9 + coef9_reg;
    end
end

endmodule

顶层模块

module lsm_top(
    (* io_buffer_type = "none" *)input clk_i,
    (* io_buffer_type = "none" *)input rst_n_i,
    (* io_buffer_type = "none" *)input signed [15:0] data_in,        //输入待滤波数据
    (* io_buffer_type = "none" *)input signed [15:0] data_ref,       //参考数据
    (* io_buffer_type = "none" *)output signed [15:0] error_o,         //误差
    (* io_buffer_type = "none" *)output signed [15:0] data_o         //输出数据
    );

    wire signed [15:0] coef1;
    wire signed [15:0] coef2;
    wire signed [15:0] coef3;
    wire signed [15:0] coef4;
    wire signed [15:0] coef5;
    wire signed [15:0] coef6;
    wire signed [15:0] coef7;
    wire signed [15:0] coef8;
    wire signed [15:0] coef9;



    fir 
    fir_dut (
      .clk_i (clk_i ),
      .rst_n_i (rst_n_i ),
      .data_in (data_in ),
      .coef1 (coef1 ),
      .coef2 (coef2 ),
      .coef3 (coef3 ),
      .coef4 (coef4 ),
      .coef5 (coef5 ),
      .coef6 (coef6 ),
      .coef7 (coef7 ),
      .coef8 (coef8 ),
      .coef9 (coef9 ),
      .data_o(data_o)
    );
  

    error_calcu 
    error_calcu_dut (
      .clk_i (clk_i ),
      .rst_n_i (rst_n_i ),
      .data_in (data_in ),
      .data_ref (data_ref ),
      .error_o  ( error_o)
    );

    
    
    coef_update 
    coef_update_dut (
      .clk_i (clk_i ),
      .rst_n_i (rst_n_i ),
      .error_o (error_o ),
      .data_in (data_in ),
      .coef1 (coef1 ),
      .coef2 (coef2 ),
      .coef3 (coef3 ),
      .coef4 (coef4 ),
      .coef5 (coef5 ),
      .coef6 (coef6 ),
      .coef7 (coef7 ),
      .coef8 (coef8 ),
      .coef9 (coef9 )
    );
  
    
    
endmodule

你可能感兴趣的:(fpga,算法,算法,fpga开发,dsp开发)