Verilog功能模块——取滑动平均值(使用FIFO)

前言

我的另一篇博客:Verilog功能模块——取滑动平均值(使用寄存器组)

两者用不同的方式实现相同的功能,

  • 使用FIFO占用较少寄存器资源,适用于取值N较大的场合。
  • 使用寄存器组不需要额外的IP,更简单,但消耗寄存器资源较多,适用于取值N较小(一般不大于1024)的场合。

一. 模块功能与应用场景

模块功能:对输入信号取滑动平均值。

滑动平均值:又名移动平均值,在简单平均值的基础上,通过顺序逐期增加新数据、减去旧数据求算移动平均值,借以消除偶然变动因素。

参考百度百科:滑动平均法

应用场景:

  • 对平均值会变化,但变化速度较慢的信号求平均值
  • 数字滤波中去除信号的直流偏置

二. 模块框图与使用说明

getMovingAvg_useFIFO

参数N表示求N个点的平均值,参数DIN_WIDTH控制输入信号位宽。

注意:

  1. din与din_valid应对齐
  2. clk应就是din与din_valid产生的时钟,这是为了保证一个有效数据din_valid只持续一个时钟周期的高电平
  3. N应为2的n次方,如:128(27),256(28),512(29),1024(210)等
  4. 为使平均值计算正确,N至少应包含一个完整信号周期的点,一般要大于2~3个信号周期的点数
  5. 这里的ac_signal为din - moving_avg表示不带直流偏置的交流信号
  6. 此模块需要搭配一个首字直通的单时钟FIFO使用,此FIFO的时钟应与此模块时钟相同
  7. FIFO深度和位宽需要与模块参数N和DIN_WIDTH分别保持一致

模块封装为IP后的框图:

模块IP参数配置界面:


三. 模块代码

/*
 * @Author       : Xu Dakang
 * @Email        : XudaKang_up@qq.com
 * @Date         : 2021-04-14 16:14:46
 * @LastEditors  : Xu Dakang
 * @LastEditTime : 2021-05-19 22:34:24
 * @Filename     : getMovingAvg.sv
 * @Description  : 求N个有符号数的滑动平均值
*/

/*
! 模块功能: 求N个有符号数的滑动平均值
* 思路:
  1.求N个数的和,再除以N即为N个数的平均值
  2.当第N+1个数到来后, 总和去除第1个数再加上第N+1个数, 再求平均值即为1~N+1个数的平均值, 以此类推
*/



module getMovingAvg
#(
  parameter N = 1024,      // 求N个数的平均值,N应等于2的n次方,如512,1024,2048等
  parameter DIN_WIDTH = 24 // 输入数据的位宽
)(
  output logic signed [DIN_WIDTH-1 : 0] moving_avg,

  output logic signed [DIN_WIDTH-1 : 0] ac_signal,
  output logic                          ac_signal_valid,

  input  logic signed [DIN_WIDTH-1 : 0] din,
  input  logic                          din_valid,

  input  logic signed [DIN_WIDTH-1 : 0] fwft_fifo_dout,
  output logic                          fwft_fifo_rd_en,
  input  logic                          fwft_fifo_empty,

  output logic signed [DIN_WIDTH-1 : 0] fwft_fifo_din,
  output logic                          fwft_fifo_wr_en,
  input  logic                          fwft_fifo_full,

  input  logic clk,
  input  logic rstn
);



//< 信号同步 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic signed [DIN_WIDTH-1 : 0] din_r1;
logic signed [DIN_WIDTH-1 : 0] din_r2;
always_ff @(posedge clk) begin
  din_r1 <= din;
  din_r2 <= din_r1;
end


logic din_valid_r1;
logic din_valid_r2;
always_ff @(posedge clk) begin
  din_valid_r1 <= din_valid;
  din_valid_r2 <= din_valid_r1;
end
//< 信号同步 ------------------------------------------------------------



//> FWFT FIFO存储N个数 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic [$clog2(N) : 0] din_cnt;
always_ff @(posedge clk) begin  // FIFO写入计数
  if (~rstn)
    din_cnt <= '0;
  else if (din_valid_r2 && din_cnt[$clog2(N)] != 1)
    din_cnt <= din_cnt + 1'b1;
  else
    din_cnt <= din_cnt;
end


// fwft FIFO输出
always_comb begin
  fwft_fifo_din = din_r2;
  fwft_fifo_wr_en = din_valid_r2;

  fwft_fifo_rd_en = din_cnt[$clog2(N)] == 1 && din_valid_r2;
end
//> FWFT FIFO存储N个数 ------------------------------------------------------------



//< 求和 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic signed [$clog2(N)+DIN_WIDTH-1 : 0] sum;
always_ff @(posedge clk) begin
  if (~rstn)
    sum <= '0;
  else if (din_valid_r2)
    if (din_cnt[$clog2(N)] != 1)
      sum <= din_r2 + sum; // 先减再加,没有位宽溢出的风险
    else
      sum <= (din_r2 - fwft_fifo_dout) + sum;
  else
    sum <= sum;
end
//< 求和 ------------------------------------------------------------



//> 输出平均值 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always_ff @(posedge clk) begin
  if (~rstn)
    moving_avg <= '0;
  else
    moving_avg <= sum[$clog2(N)+DIN_WIDTH-1 : $clog2(N)]; // 取高数据位
end
//> 输出平均值 ------------------------------------------------------------



//< 求AC信号 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always_ff @(posedge clk) begin
  if (~rstn)
    ac_signal <= '0;
  else if (din_valid_r2)
    ac_signal <= din_r2 - moving_avg;
  else
    ac_signal <= ac_signal;
end


always_ff @(posedge clk) begin
  if (~rstn)
    ac_signal_valid <= '0;
  else
    ac_signal_valid <= din_valid_r2;
end
//< 求AC信号 ------------------------------------------------------------



endmodule

四. testbench

/*
 * @Author       : Xu Dakang
 * @Email        : XudaKang_up@qq.com
 * @Date         : 2021-04-21 14:29:04
 * @LastEditors  : Xu Dakang
 * @LastEditTime : 2021-05-20 00:32:15
 * @Filename     : getMovingAvg_tb.sv
 * @Description  : testbench of getMovingAvg
*/



module getMovingAvg_tb;

timeunit 1ns;
timeprecision 1ps;


localparam DIN_WIDTH = 24;

logic [DIN_WIDTH-1 : 0] ac_signal;
logic ac_signal_valid;

logic [DIN_WIDTH-1 : 0] din;
logic din_valid;

logic [DIN_WIDTH-1 : 0] moving_avg;

logic clk;
logic rstn;

getMovingAvg_system_wrapper  getMovingAvg_system_wrapper_inst (.*);



// 生成时钟
localparam CLKT = 2;
initial begin
  clk = 0;
  forever #(CLKT / 2) clk = ~clk;
end



// 导入输入波形文件 Vivado只能识别绝对路径 注意修改!!!
string din_path = "F:/OneDrive/VivadoPrj/getMovingAvg_useFIFO/getMovingAvg_useFIFO.srcs/sim_1/new/sin+0.5.txt"; // 可选 sin  sin+0.5  sin-0.5  sin+1.0  sin-1.0

localparam DATA_NUM = 10240; // 数据量, 也就是txt文件的行数, 如果此参数大于数据行数, 读取到的内容为不定态
logic [DIN_WIDTH-1 : 0] din_wave_data [DATA_NUM]; // 读取输入波形数据

initial begin
  $readmemb(din_path, din_wave_data, 0, DATA_NUM-1); // vivado读取txt文件
end


int cnt;
initial begin
  rstn = 0;
  din_valid = 0;
  #(CLKT * 10)  rstn = 1;

  for (int i = 0; i < DATA_NUM; i++) begin
    din = din_wave_data[i];
    cnt++;
    din_valid = 1;
    #(CLKT);
    // din_valid = 0;
    // #(CLKT * ({$random} % 12));
  end
  din_valid = 0;

  #(CLKT * 10) $stop;
end



endmodule

五. 仿真验证

仿真工具:Vivado 2020.2 Simulator。

lock Design如下图,注意:

  1. 模块的参数N与FIFO深度保持一致
  2. 模块数据位宽与FIFO数据位宽保持一致
  3. FIFO必须是FWFT类型的
  1. 输入为(sinx + 0.5)信号:
  1. 输入为(sinx - 0.5)信号:
  1. 输入为(sinx + 1.0)信号:
  1. 输入为(sinx - 1.0)信号:

六. 工程分享

getMovingAvg_useFIFO 取滑动平均值模块(使用FIFO) vivado 2020.2工程.7z。

欢迎大家关注我的公众号:徐晓康的博客,回复以下代码获取。

1653

建议复制过去不会码错字!


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。

  • 10
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
滑动平均滤波在Verilog中可以通过使用FIFO(First-In-First-Out)模块来实现。FIFO是一种在数据流中按照先进先出原则进行数据存储和读的数据结构。通过FIFO,我们可以实现滑动平均的计算。 在Verilog中,可以通过以下步骤来实现滑动平均的计算: 1. 创建一个FIFO模块,用于存储输入的数据。该FIFO模块应具有适当的读写接口,以便可以按照先进先出的原则进行数据存储和读。 2. 设计一个计数器,用于计算已经输入到FIFO中的数据量。当达到所需的数据量时,可以开始计算滑动平均。 3. 使用适当的算法从FIFO中读数据,并计算平均。可以使用滑动算术平均或者滑动加权平均,具体决于需求。 4. 输出计算得到的滑动平均。 需要注意的是,具体的Verilog代码实现可能因应用场景和需求的不同而有所差异。以上步骤仅提供了一个基本的框架,您可以根据具体的需求进行进一步的开发和调整。 引用提供了关于滑动平均的程序设计关键点,引用则解释了滑动平均的概念和用途,引用提供了一个使用FIFO实现滑动平均Verilog工程示例。请参考这些引用内容以进一步了解滑动平均Verilog实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [滑动平均滤波的VERILOG实现](https://blog.csdn.net/mcupro/article/details/121392135)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Verilog功能模块——滑动平均使用FIFO)](https://blog.csdn.net/weixin_42837669/article/details/117094472)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值