滑动平均值滤波的VERILOG实现

66 篇文章 23 订阅
58 篇文章 27 订阅


 滑动平均值滤波是指先在RAM中建立一个数据缓冲区,依顺序存放N个采样数据,每采进一个新数据,就将最早采集的那个数据丢掉,而后求包括新数据在内的N个数据的算术平均值或加权平均值。这样,每进行一次采样,就可计算出一个新的平均值,从而加快了数据处理的速度。 

​滑动平均值滤波程序设计的关键是:每采样一次,移动一次数据块,然后求出新一组数据之和,再求平均值。滑动平均值滤波程序有两种,一种是滑动算术平均值滤波,一种是滑动加权平均值滤波。不管是算术平均值滤波,还是加权平均值滤波,都需连续采样N个数据,然后求算术平均值或加权平均值。

上述两段文字来自百度。这里做的是算术平均值滤波。以下代码来自openofdm的moving_avg.v文件,就是严格按照这个思路写的。

module moving_avg
#(
    parameter DATA_WIDTH = 32,
    parameter WINDOW_SHIFT = 4,
    parameter SIGNED = 0
)
(
    input clock,
    input enable,
    input reset,

    input signed [DATA_WIDTH-1:0] data_in,
    input input_strobe,

    output reg signed [DATA_WIDTH-1:0] data_out,
    output reg output_strobe
);

localparam WINDOW_SIZE = 1<<WINDOW_SHIFT;
localparam SUM_WIDTH = DATA_WIDTH + WINDOW_SHIFT;

reg signed [(SUM_WIDTH-1):0] running_sum;

wire signed [DATA_WIDTH-1:0] old_data;
wire signed [DATA_WIDTH-1:0] new_data = data_in;

wire signed [SUM_WIDTH-1:0] ext_old_data = {{WINDOW_SHIFT{old_data[DATA_WIDTH-1]}}, old_data};
wire signed [SUM_WIDTH-1:0] ext_new_data = {{WINDOW_SHIFT{new_data[DATA_WIDTH-1]}}, new_data};


reg [WINDOW_SHIFT-1:0] addr;
reg full;

ram_2port  #(.DWIDTH(DATA_WIDTH), .AWIDTH(WINDOW_SHIFT)) delay_line (
    .clka(clock),
    .ena(1),
    .wea(input_strobe),
    .addra(addr),
    .dia(data_in),
    .doa(),
    .clkb(clock),
    .enb(input_strobe),
    .web(0),
    .addrb(addr),
    .dib(32'hFFFF),
    .dob(old_data)
);

integer i;
always @(posedge clock) begin
    if (reset) begin
        addr <= 0;
        running_sum <= 0;
        full <= 0;
        data_out <= 0;
    end else if (enable) begin
        if (input_strobe) begin
            addr <= addr + 1;
            data_out <= running_sum[SUM_WIDTH-1:WINDOW_SHIFT];

            if (addr == WINDOW_SIZE-1) begin
                full <= 1;
            end

            if (full) begin
                running_sum <= running_sum + ext_new_data- ext_old_data;
            end else begin
                running_sum <= running_sum + ext_new_data;
            end

            output_strobe <= full;
        end else begin
            output_strobe <= 0;
        end
    end else begin
        output_strobe <= 0;
    end
end
endmodule

这里我们sum记录了ram里面的所存的前WORD_SIZE-1个数值的算术和。当到达这些位数之后,每输入一位就有一位溢出(或者说被移除),溢出的序号正好等于输入的那个序号,好让位该空间用于存储。可见这里要求ram_2port读的延迟必须是一个周期,写的延迟呢,应该是小于WINDOW_SHIFT就好,WINDOWS_SHIFT最小可以是2,那么写延迟最小可以是1.因此我们也就实现一级写延迟。一级读写延迟的RAM_2PORT实现代码如下:

//
// Copyright 2011 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//



module ram_2port
#(
    parameter DWIDTH=32,
    parameter AWIDTH=9
)
(
    input clka,
    input ena,
    input wea,
    input [AWIDTH-1:0] addra,
    input [DWIDTH-1:0] dia,
    output reg [DWIDTH-1:0] doa,

    input clkb,
    input enb,
    input web,
    input [AWIDTH-1:0] addrb,
    input [DWIDTH-1:0] dib,
    output reg [DWIDTH-1:0] dob
);

reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
integer 	    i;
initial begin
    for(i=0;i<(1<<AWIDTH);i=i+1)
        ram[i] <= {DWIDTH{1'b0}};
    doa <= 0;
    dob <= 0;
end

always @(posedge clka) begin
    if (ena) 
    begin
        if (wea)
            ram[addra] <= dia;
        doa <= ram[addra];
    end
end
always @(posedge clkb) begin
    if (enb)
    begin
        if (web)
            ram[addrb] <= dib;
        dob <= ram[addrb];
    end
end
endmodule // ram_2port

这份RAM_2PORT代码来自ETTUS的USRP产品的源码,被OPENOFDM引用。

作为效果我看看看仿真图

 这是在openofdm里面内嵌的一个4平均值的滑动滤波器效果,整个就是openofdm正常实例仿真的数据。我们看到蓝色输出相对与绿色的输入确实平滑了很多。

这里这个32位4平均数值的模块实现过于麻烦了,一大堆寄存器和一个双口RAM。其实对于2,4,8,16这些2的数次方的特例是有可以用另外使用移位寄存器。思路我用代码简单写写核心部分如下:


reg [31:0] r0,r1,r2,r3;
reg [31+2:0]sum;
always @ (posedge clk ) if (en) {r3,r2,r1,r0}<={r2,r1,r0,sig_in} ;
always @ (posedge clk )sum <= r3+r2+r1+r0 ;
assign sig_out = sum [31+2:2] ;
 

这里注意输出实际是左移动2位后的sum,也就是sum除以4。如果要滑动5位就是能这样左移位了,在verilog里面实现除以5的代价远不是加减肥那样简单,所以要再次评估实现方式了。

如果需要out_storbe信号,就将in_strobe和sig_in一起按照时序移动就好。

话回来,现在FPGA都资源冗余,具体工程中也没有必要在实现细节上死磕资源。具体使用时候可以根据情况权衡实现方式。

  • 4
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值