【Verilog 教程】7.3 Verilog 串行 FIR 滤波器设计

串行 FIR 滤波器设计
设计说明

设计参数不变,与并行 FIR 滤波器参数一致。即,输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KMHz 的信号。

输入频率:    7.5MHz 和 250KHz
采样频率:    50MHz
阻带:           1MHz-6MHz
阶数:           15 (N=15

串行设计,就是在 16 个时钟周期内对 16 个延时数据分时依次进行乘法、加法运算,然后在时钟驱动下输出滤波值。考虑到 FIR 滤波器系数的对称性,计算一个滤波输出值的周期可以减少到 8 个。串行设计时每个周期只进行一次乘法运算,所以设计中只需一个乘法器即可。此时数据需要每 8 个时钟周期有效输入一次,但是为了保证输出信号频率的正确性,工作时钟需要为采样频率的 8 倍,即 400MHz。这种方法的优点是资源耗费少,但是工作频率要求高,数据不能持续输出。

串行设计

设计中使用到的乘法器模块代码,可参考之前流水线式设计的乘法器。

为方便快速仿真,也可以直接使用乘号 * 完成乘法运算,设计中加入宏定义 SAFE_DESIGN 来选择使用哪种乘法器。

FIR 滤波器系数可由 matlab 生成,具体见附录。

/**********************************************************
>> Description : fir study with serial tech
>> V190403     : Fs:50Mhz, fstop:1-6Mhz, order:16, sys clk:400MHz
***********************************************************/
`define SAFE_DESIGN
 
module fir_serial_low(
    input                rstn,
    input                clk,   // 系统工作时钟,400MHz
    input                en ,   // 输入数据有效信号
    input        [11:0]  xin,   // 输入混合频率的信号数据
    output               valid, // 输出数据有效信号
    output       [28:0]  yout   // 输出数据
    );
 
   //delay of input data enable
    reg [11:0]            en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r[11:0]      <= 'b0 ;
        end
        else begin
            en_r[11:0]      <= {en_r[10:0], en} ;
        end
    end
 
    //fir coeficient
    wire        [11:0]   coe[7:0] ;
    assign coe[0]        = 12'd11 ;
    assign coe[1]        = 12'd31 ;
    assign coe[2]        = 12'd63 ;
    assign coe[3]        = 12'd104 ;
    assign coe[4]        = 12'd152 ;
    assign coe[5]        = 12'd198 ;
    assign coe[6]        = 12'd235 ;
    assign coe[7]        = 12'd255 ;
  
    //(1) 输入数据移位部分 
    reg [2:0]            cnt ;
    integer              i, j ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt <= 3'b0 ;
        end
        else if (en || cnt != 0) begin
            cnt <= cnt + 1'b1 ;    //8个周期计数
        end
    end
 
    reg [11:0]           xin_reg[15:0];
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            for (i=0; i<16; i=i+1) begin
                xin_reg[i]  <= 12'b0;
            end
        end
        else if (cnt == 3'd0 && en) begin    //每8个周期读入一次有效数据
            xin_reg[0] <= xin ;
            for (j=0; j<15; j=j+1) begin
                xin_reg[j+1] <= xin_reg[j] ; // 数据移位
            end
        end
    end
  
    //(2) 系数对称,16个移位寄存器数据进行首位相加
    reg  [11:0]          add_a, add_b ;
    reg  [11:0]          coe_s ;
    wire [12:0]          add_s ;
    wire [2:0]           xin_index = cnt>=1 ? cnt-1 : 3'd7 ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            add_a  <= 13'b0 ;
            add_b  <= 13'b0 ;
            coe_s  <= 12'b0 ;
        end
        else if (en_r[xin_index]) begin //from en_r[1]
            add_a  <= xin_reg[xin_index] ;
            add_b  <= xin_reg[15-xin_index] ;
            coe_s  <= coe[xin_index] ;
        end
    end
    assign add_s = {add_a} + {add_b} ;  
 
    //(3) 乘法运算,只用一个乘法
    reg        [24:0]    mout ;
`ifdef SAFE_DESIGN
    wire                 en_mult ;
    wire [3:0]           index_mult = cnt>=2 ? cnt-1 : 4'd7 + cnt[0] ;
    mult_man #(13, 12)   u_mult_single    //例化自己设计的流水线乘法器
        (.clk        (clk),
         .rstn       (rstn),
         .data_rdy   (en_r[index_mult]),  //注意数据时序对应
         .mult1      (add_s),
         .mult2      (coe_s),
         .res_rdy    (en_mult),   
         .res        (mout)
        );
 
`else
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            mout   <= 25'b0 ;
        end
        else if (|en_r[8:1]) begin
            mout   <= coe_s * add_s ;  //直接乘
        end
    end
    wire                 en_mult = en_r[2];
`endif
 
    //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据
    reg        [28:0]    sum ;
    reg                  valid_r ;
    //mult output en counter
    reg [4:0]            cnt_acc_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt_acc_r <= 'b0 ;
        end
        else if (cnt_acc_r == 5'd7) begin  //计时8个周期
            cnt_acc_r <= 'b0 ;
        end
        else if (en_mult || cnt_acc_r != 0) begin //只要en有效,计时不停
            cnt_acc_r <= cnt_acc_r + 1'b1 ;
        end
    end
 
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            sum      <= 29'd0 ;
            valid_r  <= 1'b0 ;
        end
        else if (cnt_acc_r == 5'd7) begin //在第8个累加周期输出滤波值
            sum      <= sum + mout;
            valid_r  <= 1'b1 ;
        end
        else if (en_mult && cnt_acc_r == 0) begin //初始化
            sum      <= mout ;
            valid_r  <= 1'b0 ;
        end
        else if (cnt_acc_r != 0) begin //acculating between cycles
            sum      <= sum + mout ;
            valid_r  <= 1'b0 ;
        end
    end
 
    //时钟锁存有效的输出数据,为了让输出信号不是那么频繁的变化
    reg [28:0]           yout_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            yout_r <= 'b0 ;
        end
        else if (valid_r) begin
            yout_r <= sum ;
        end
    end
    assign yout = yout_r ;
 
    //(5) 输出数据有效延迟,即滤波数据丢掉前15个滤波值
    reg [4:0]    cnt_valid ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt_valid      <= 'b0 ;
        end
        else if (valid_r && cnt_valid != 5'd16) begin
            cnt_valid      <= cnt_valid + 1'b1 ;
        end
    end
    assign valid = (cnt_valid == 5'd16) & valid_r ;

endmodule

testbench

testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体见附录。

其中,工作频率为 400MHz,但输入数据和输入数据有效信号应当都保持 50MHz 的频率输入。

module test ;
    //input
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg  [11:0]  xin ;
    //output
    wire [28:0]  yout ;
    wire         valid ;

    parameter    SIMU_CYCLE   = 64'd1000 ;
    parameter    SIN_DATA_NUM = 200 ;

//=====================================
// 8*50MHz clk generating
    localparam   TCLK_HALF     = (10_000 >>3);
    initial begin
        clk = 1'b0 ;
        forever begin
            # TCLK_HALF clk = ~clk ;
        end
     end
 
//============================
//  reset and finish
    initial begin
        rst_n = 1'b0 ;
        # 30        rst_n = 1'b1 ;
        # (TCLK_HALF * 2 * 8  * SIMU_CYCLE) ;
        $finish ;
    end
 
//=======================================
// read cos data into register
    reg          [11:0] stimulus [0: SIN_DATA_NUM-1] ;
    integer      i ;
    initial begin
        $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ;
        en = 0 ;
        i = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            repeat(7)  @(negedge clk) ; //空置7个周期,第8个周期给数据
            en          = 1 ;
            xin         = stimulus[i] ;
            @(negedge clk) ;
            en          = 0 ;         //输入数据有效信号只持续一个周期即可
            if (i == SIN_DATA_NUM-1)  i = 0 ;
            else  i = i + 1 ;
        end 
    end 
 
    fir_serial_low       u_fir_serial (
        .clk         (clk),
        .rstn        (rst_n),
        .en          (en),
        .xin         (xin),
        .valid       (valid),
        .yout        (yout));

endmodule

仿真结果

由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。为了波形更加的美观,取 16 个之后的滤波数据作为有效输出。
在这里插入图片描述
波形局部放大后如下图所示,此时输入数据有效信号 en 与输出数据有效信号 valid 是周期(50MHz)相同的脉冲信号,不是持续有效的。但工作时钟为 400MHz,所以输出也会呈现为 50MHz 采样频率下的 250KHz 频率的正弦波信号。
在这里插入图片描述

附录:matlab 使用(与《并行 FIR 滤波器设计》一致)
生成 FIR 滤波器系数

打开 matlab,在命令窗口输入命令: fdatool。

然后会打开如下窗口,按照 FIR 滤波器参数进行设置,如下所示。

这里选择的 FIR 实现方法是最小二乘法(Least-squares),不同的实现方式滤波效果也不同。
在这里插入图片描述
点击 File -> Export

将滤波器参数输出,存到变量 coef 中,如下图所示。
在这里插入图片描述

此时 coef 变量应该是浮点型数据。对其进行一定倍数的相乘扩大,然后取其近似的定点型数据作为设计中的 FIR 滤波器参数。这里取扩大倍数为 2048,结果如下所示。

在这里插入图片描述

生成输入的混合信号

利用 matlab 生成混合的输入信号参考代码如下。

信号为无符号定点型数据,位宽宽度为 12bit,存于文件 cosx0p25m7p5m12bit.txt 。

clear all;close all;clc;
%=======================================================
% generating a cos wave data with txt hex format
%=======================================================

fc          = 0.25e6 ;      % 中心频率
fn          = 7.5e6 ;       % 杂波频率
Fs          = 50e6 ;        % 采样频率
T           = 1/fc ;        % 信号周期
Num         = Fs * T ;      % 周期内信号采样点数
t           = (0:Num-1)/Fs ;      % 离散时间
cosx        = cos(2*pi*fc*t) ;    % 中心频率正弦信号
cosn        = cos(2*pi*fn*t) ;    % 杂波信号
cosy        = mapminmax(cosx + cosn) ;     %幅值扩展到(-1,1) 之间
cosy_dig    = floor((2^11-1) * cosy + 2^11) ;     %幅值扩展到 0~4095
fid         = fopen('cosx0p25m7p5m12bit.txt', 'wt') ;  %写数据文件
fprintf(fid, '%x\n', cosy_dig) ;
fclose(fid) ;
 
%时域波形
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
 
%频域波形
fft_cosy    = fftshift(fft(cosy, Num)) ;
f_axis      = (-Num/2 : Num/2 - 1) * (Fs/Num) ;
figure(5) ;
plot(f_axis, abs(fft_cosy)) ;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/113264.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 省市区三级联动选择器

还有半个小时下班&#xff0c;总想着发点光亮照耀他人。IT技术这东西&#xff0c;尤其是UI方面的东西&#xff0c;于用户体验至关重要&#xff0c;想想最近使用uni-data-picker的丑陋页面&#xff0c;自己重构了这个功能&#xff0c;新加实现&#xff0c;效果图如下&#xff0c…

使用 Curl 和 DomCrawler 下载抖音视频链接并存储到指定文件夹

项目需求 假设我们需要从抖音平台上下载一些特定的视频&#xff0c;以便进行分析、编辑或其他用途。为了实现这个目标&#xff0c;我们需要编写一个爬虫程序来获取抖音视频的链接&#xff0c;并将其保存到本地文件夹中。 目标分析 在开始编写爬虫之前&#xff0c;我们需要了…

【Linux】配置JDKTomcat开发环境及MySQL安装和后端项目部署

目录 一、jdk安装配置 1. 传入资源 2. 解压 3. 配置 二、Tomcat安装 1. 解压开启 2. 开放端口 三、MySQL安装 1. 解压安装 2. 登入配置 四、后端部署 1. 数据库 2. 导入.war包 3. 修改端口 4.开启访问 一、jdk安装配置 打开虚拟机 Centos 登入账号&#xff…

MySQL -- 内置函数

MySQL – 内置函数 文章目录 MySQL -- 内置函数一、日期函数1.current_date()获取年月日2.current_time()获取时分秒3.current_timestamp() / now()获得时间戳4.date_add()在日期的基础上加日期5.date_sub()在日期的基础上减去日期6. datediff()计算两个日期之间相差多少天7.案…

inquirer.js——交互式命令行用户界面

一、什么是inquirer.js 1、inquirer.js是一个开源的交互式命令行用户界面&#xff08;CLI&#xff09;库&#xff0c;可以让你轻松地与用户进行交互&#xff0c;获取用户输入并做出相应的处理。它的主要功能是提供了一系列常用的命令行交互界面组件&#xff0c;例如input、con…

【Redis】高并发分布式结构服务器

文章目录 服务端高并发分布式结构名词基本概念评价指标1.单机架构缺点 2.应用数据分离架构应用服务集群架构读写分离/主从分离架构引入缓存-冷热分离架构分库分表&#xff08;垂直分库&#xff09;业务拆分⸺微服务 总结 服务端高并发分布式结构 名词基本概念 应⽤&#xff0…

Docker的安装、基础命令与项目部署

文章目录 前言一、docker安装与MySQL部署1.Linux环境下docker的安装&#xff08;1&#xff09;基于CentOS7&#xff08;2&#xff09;基于Ubuntu 二、docker基础1.常见命令&#xff08;1&#xff09;快速创建一个mysql容器&#xff08;MySQL得一键安装&#xff09;。&#xff0…

centos7 部署 Flink

1. 准备 安装的前提是虚拟机里已安装了jdk 去官网下载 Flink 所有版本下载地址&#xff1a;https://archive.apache.org/dist/flink/ 找到下图的安装包&#xff0c;下载即可 下载完后&#xff0c;将其上传至虚拟机的某个地方&#xff0c;本人将其放在 /home/flink/ 下 解压…

Learning to Segment Rigid Motions from Two Frames 代码复现

环境配置 https://github.com/gengshan-y/rigidmask 1.拉取代码 git clone https://github.com/gengshan-y/rigidmask.git cd rigidmask2.创建conda环境&#xff0c;修改rigidmask.yml name: rigidmask channels:- pytorch- pytorch3d- conda-forge- defaults dependencies…

java--构造器

1.构造器是什么样子 构造器分为无参构造(就相当于你有车子&#xff0c;但是里面是空的)和带参构造(就相当于你有车子&#xff0c;里面还有几个妹纸&#xff0c;你真该死啊) 2.构造器有什么特点 创建对象时&#xff0c;对象会去调用构造器。 3.构造器的常见应用场景 创建对象…

Maven进阶系列-继承和聚合

Maven进阶系列-继承和聚合 文章目录 Maven进阶系列-继承和聚合1. 继承2. 继承的作用2.1 在父工程中配置依赖的统一管理2.2 在父工程中声明自定义属性2.3 父工程中必须要继承的配置 3. 聚合4. 聚合的作用 1. 继承 Maven工程之间存在继承关系&#xff0c;例如工程B继承工程A&…

基于Qt QTreeView|QTreeWidget控件使用简单版

头文件解析: 这是一个C++代码文件,定义了一个名为MainWindow的类。以下是对每一句的详细解释: ```cpp #ifndef MAINWINDOW_H #define MAINWINDOW_H ``` 这是一个条件编译指令,用于避免头文件的重复包含。`MAINWINDOW_H`是一个宏定义,用于唯一标识这个头文件。 ```cpp #…

idea的设置

1.设置搜索encoding,所有编码都给换为utf-8 安装插件 eval-reset插件 https://www.yuque.com/huanlema-pjnah/okuh3c/lvaoxt#m1pdA 设置活动模板,idea有两种方式集成tomcat,一种是右上角config配置本地tomcat,一种是插件,如果使用插件集成,则在maven,pom.xml里面加上tomcat…

【TES720D】青翼科技基于复旦微的FMQL20S400全国产化ARM核心模

板卡概述 TES720D是一款基于上海复旦微电子FMQL20S400的全国产化核心模块。该核心模块将复旦微的FMQL20S400&#xff08;兼容FMQL10S400&#xff09;的最小系统集成在了一个50*70mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;特别是用…

AI:49-基于深度学习的杂草识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

架构设计之大数据架构(Lambda架构、Kappa架构)

大数据架构 一. 大数据技术生态二. 大数据分层架构三. Lambda架构3.1 Lambda架构分解为三层3.2 优缺点3.3 实际案例 四. Kappa架构4.1 结构图4.2 优缺点4.3 实际案例 五. Lambda架构与Kappa架构对比 其它相关推荐&#xff1a; 系统架构之微服务架构 系统架构设计之微内核架构 鸿…

diffusers-Load adapters

https://huggingface.co/docs/diffusers/main/en/using-diffusers/loading_adaptershttps://huggingface.co/docs/diffusers/main/en/using-diffusers/loading_adapters 有几种训练技术可以个性化扩散模型&#xff0c;生成特定主题的图像或某些风格的图像。每种训练方法都会产…

zookeeper集群选举机制

Zookeeper选举机制——第一次启动 zookeeper集群三个重要的参数&#xff08;决定选举结果&#xff09; SID &#xff1a; 服务器 ID 。 用来唯一标识一台 ZooKeeper集群中的机器&#xff0c;每台机器不能重 &#xff0c; 和 myid 一致 。 ZXID &#xff1a;事务 ID 。 ZXID 是…

开发项目管理必备神器!10款好用的在线看板工具推荐!

在当今高速发展、竞争激烈的商业环境中&#xff0c;项目管理已经成为了许多组织的生存之道。项目管理可以有效地协调资源&#xff0c;监控进度&#xff0c;把握风险&#xff0c;并实现组织的策略目标。然而&#xff0c;传统的项目管理方法已经无法满足日益复杂和变化的工作需求…

常用 sqlite3 命令

本次将向您讲解 SQLite 编程人员所使用的简单却有用的命令。这些命令被称为 SQLite 的点命令&#xff0c;这些命令的不同之处在于它们不以分号 ; 结束。 让我们在命令提示符下键入一个简单的 sqlite3 命令&#xff0c;在 SQLite 命令提示符下&#xff0c;您可以使 用各种 …
最新文章