FPGA 的数字信号处理:重写 FIR 逻辑以满足时序要求

news2025/1/18 19:09:43

aca4673ec2ffebc5d804b8cdcb554ceb.jpeg

97447a27a218c3b3a8eb1f85a488cb58.png

在上一篇文章中(FPGA 的数字信号处理:Verilog 实现简单的 FIR 滤波器)演示了在 Verilog 中编写自定义 FIR 模块的初始demo。该项目在行为仿真中正常,但在布局和布线时未能满足时序要求。

所以今天的文章让我们来看看当设计不能满足时序要求时如何分析并解决它。

af3703f59a80142d3278853e29794715.jpeg

当在目标 FPGA 芯片中布局和布线时,首先在 Vivado 中确定时序要求.

将 FIR 作为RTL 模块导入到block design中,其中通过AXI DMA 从存储器传输相位增量偏移值的DDS可以输入可变频率正弦曲线,这样就可以演示FIR的行为。

4f9749648dcece0511b432268d77fda7.png

在 Vivado 中综合布局布线并打开设计后,会弹出严重警告,告知设计不符合时序要求。

为了能够准确查看设计时序失败的原因,在已完成综合设计的底部窗口包含一个选项卡,用于 Vivado 在综合期间对设计执行的时序分析。当存在时序失败的信号路径时,用户可以过滤此时序分析以仅使用下图中显示的红色圆圈感叹号查看这些违规路径:

3ec85a67e2762f0fc06b03a89a3ad8d1.png

在这个特定的设计中,有几个信号路径未能达到其分配的时序,这意味着信号的物理距离太远而无法穿过芯片和/或在信号出去之前需要通过太多的逻辑级别。保持时间太长的信号意味着当将其计时到下一级寄存器中时,不能依赖它的值是否有效,从而使其余下游逻辑的行为不可靠/不可预测。

s_axis_fir_tdata在这种情况下,进入 FIR 模块的 AXI Stream 输入接口的数据信号需要很长时间才能到达m_axis_fir_tdata目标寄存器处的输出。要查看比屏幕底部的时序分析窗口中的内容更多的详细信息,右键单击底部时序分析窗口中的违规信号路径,然后选择“查看路径报告(View Path Report)”选项。然后,将能够看到 Vivado 如何计算出该信号的允许建立时间,并与它实际给出的 HDL 设计编写方式进行比较。这会给一些提示,说明是什么导致建立时间延长。然而,我发现要真正可视化保持时序违规比在示意图中查看信号会更直观。

da5e0155b892584ad44598b89e10ff7e.png

要在原理图中打开特定信号路径,再次右键单击底部时序分析窗口中的违规信号路径,然后选择“Schematic”选项。将打开一个新选项卡,显示信号路径在设计的物理布局中经过的逻辑。

在为axis_fir_tdata的数据总线中的一个位打开信号路径时,它揭示了设计在芯片中的布线,从图中可以看出信号必须通过 11 级逻辑串行后才能到达其目的地。

b142d76561d500f4bc861c5d401f4791.png

既然对已实施设计的分析已经揭示了哪些信号路径是哪个时序违规的问题,现在的问题是我们如何解决它?在这种情况下,很明显需要重新设计当前逻辑,以更并行的方式处理更小的数据块,从而缩短数据到其目标寄存器的总路径。

个人更喜欢在尝试编写任何实际的 Verilog 代码之前绘制出逻辑。当有这种设计执行的操作的可视化表示时,调试设计会容易得多,特别是对于跟踪此类时序违规等问题。

检查当前 FIR 模块的逻辑设计,其中数据总线违反了建立时序,很明显循环缓冲区串行填充然后将所有 15 个数据发送到累加块时,立即求和会产生大量的处理延迟。

50996b5d6a47345cefc2a79eb6e55944.png

核心的想法是尝试填充循环缓冲区,将每个缓冲区乘以适当的系数,最后一次性对 15 个算子的每一个求和,但是这次我们考虑重新设计逻辑,让循环缓冲区中仅花费乘法和累加(求和)两个寄存器一个级联的时间。

eb76a35ff2b8939637ab46a719d90b6c.png

新 FIR 模块的 Verilog 代码:

`timescale 1ns / 1ps

module FIR(
    input clk,
    input reset,
    input signed [15:0] s_axis_fir_tdata, 
    input [3:0] s_axis_fir_tkeep,
    input s_axis_fir_tlast,
    input s_axis_fir_tvalid,
    input m_axis_fir_tready,
    output reg m_axis_fir_tvalid,
    output reg s_axis_fir_tready,
    output reg m_axis_fir_tlast,
    output reg [3:0] m_axis_fir_tkeep,
    output reg signed [31:0] m_axis_fir_tdata
    );

    /* This loop controls tkeep signal on AXI Stream interface */
    always @ (posedge clk)
        begin
            m_axis_fir_tkeep <= 4'hf;
        end
        
    /* This loop controls tlast signal on AXI Stream interface */
    always @ (posedge clk)
        begin
            if (s_axis_fir_tlast == 1'b1)
                begin
                    m_axis_fir_tlast <= 1'b1;
                end
            else
                begin
                    m_axis_fir_tlast <= 1'b0;
                end
        end
    
    // 15-tap FIR 
    reg enable_fir;
    reg signed [15:0] buff0, buff1, buff2, buff3, buff4, buff5, buff6, buff7, buff8, buff9, buff10, buff11, buff12, buff13, buff14;
    wire signed [15:0] tap0, tap1, tap2, tap3, tap4, tap5, tap6, tap7, tap8, tap9, tap10, tap11, tap12, tap13, tap14; 
    reg signed [31:0] acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7, acc8, acc9, acc10, acc11, acc12, acc13, acc14; 

    
    /* Taps for LPF running @ 1MSps */
    assign tap0 = 16'hFC9C;  // twos(-0.0265 * 32768) = 0xFC9C
    assign tap1 = 16'h0000;  // 0
    assign tap2 = 16'h05A5;  // 0.0441 * 32768 = 1445.0688 = 1445 = 0x05A5
    assign tap3 = 16'h0000;  // 0
    assign tap4 = 16'hF40C;  // twos(-0.0934 * 32768) = 0xF40C
    assign tap5 = 16'h0000;  // 0
    assign tap6 = 16'h282D;  // 0.3139 * 32768 = 10285.8752 = 10285 = 0x282D
    assign tap7 = 16'h4000;  // 0.5000 * 32768 = 16384 = 0x4000
    assign tap8 = 16'h282D;  // 0.3139 * 32768 = 10285.8752 = 10285 = 0x282D
    assign tap9 = 16'h0000;  // 0
    assign tap10 = 16'hF40C; // twos(-0.0934 * 32768) = 0xF40C
    assign tap11 = 16'h0000; // 0
    assign tap12 = 16'h05A5; // 0.0441 * 32768 = 1445.0688 = 1445 = 0x05A5
    assign tap13 = 16'h0000; // 0
    assign tap14 = 16'hFC9C; // twos(-0.0265 * 32768) = 0xFC9C
    
    /* This loop controls tready & tvalid signals on AXI Stream interface */
    always @ (posedge clk)
        begin
            if(reset == 1'b0 || m_axis_fir_tready == 1'b0 || s_axis_fir_tvalid == 1'b0)
                begin
                    enable_fir <= 1'b0;
                    s_axis_fir_tready <= 1'b0;
                    m_axis_fir_tvalid <= 1'b0;
                end
            else
                begin
                    enable_fir <= 1'b1;
                    s_axis_fir_tready <= 1'b1;
                    m_axis_fir_tvalid <= 1'b1;
                end
        end
    
    reg [3:0] cnt;
    reg signed [31:0] acc01, acc012, acc23, acc34, acc45, acc56, acc67, acc78, acc89, acc910, acc1011, acc1112, acc1213;
    
    /* Circular buffer w/ Multiply & Accumulate stages of FIR */
    always @ (posedge clk or posedge reset)
        begin
            if (reset == 1'b0)
                begin
                    m_axis_fir_tdata <= 32'd0;
                end
            else if (enable_fir == 1'b1)
                begin
                    buff0 <= s_axis_fir_tdata;
                    acc0 <= tap0 * buff0;
                    
                    buff1 <= buff0; 
                    acc1 <= tap1 * buff1;  
                    acc01 <= acc0 + acc1;
                         
                    buff2 <= buff1; 
                    acc2 <= tap2 * buff2;
                    acc012 <= acc01 + acc2;
                            
                    buff3 <= buff2; 
                    acc3 <= tap3 * buff3;
                    acc23 <= acc012 + acc3;
                         
                    buff4 <= buff3; 
                    acc4 <= tap4 * buff4;
                    acc34 <= acc23 + acc4;
                         
                    buff5 <= buff4;
                    acc5 <= tap5 * buff5; 
                    acc45 <= acc34 + acc5;
                          
                    buff6 <= buff5; 
                    acc6 <= tap6 * buff6;
                    acc56 <= acc45 + acc6;
                       
                    buff7 <= buff6; 
                    acc7 <= tap7 * buff7;
                    acc67 <= acc56 + acc7;
                          
                    buff8 <= buff7;
                    acc8 <= tap8 * buff8;
                    acc78 <= acc67 + acc8;
                           
                    buff9 <= buff8; 
                    acc9 <= tap9 * buff9;
                    acc89 <= acc78 + acc9;
                          
                    buff10 <= buff9; 
                    acc10 <= tap10 * buff10;
                    acc910 <= acc89 + acc10;
                           
                    buff11 <= buff10; 
                    acc11 <= tap11 * buff11;
                    acc1011 <= acc910 + acc11;
                          
                    buff12 <= buff11; 
                    acc12 <= tap12 * buff12;
                    acc1112 <= acc1011 + acc12;
                          
                    buff13 <= buff12; 
                    acc13 <= tap13 * buff13;
                    acc1213 <= acc1112 + acc13;
                          
                    buff14 <= buff13; 
                    acc14 <= tap14 * buff14;
                    m_axis_fir_tdata <= acc1213 + acc14;
                    
                end
        end
    
endmodule

使用新 FIR 模块的 Verilog 代码重新运行综合布局布线后就可以产生满足所有时序要求的设计。打开之前违反建立时序的相同数据信号路径的原理图,可以直观地证明信号路径是如何整体缩短的。

新原理图显示axis_fir_tdata总线中每个位的信号路径都被并行处理,有效地缩短了它们到达目标寄存器的时间,这就是减少信号建立时间的原因。

b013aa87bbde6079eb72d40b9b88c1d9.png

当新设计满足时序要求时,接下来就是验证重写后的逻辑是否仍然与旧逻辑一样。重新运行行为仿真将很快回答这个问题。

由于希望用新代码替换之前的逻辑,发现设置断点并在更新波形图时单步执行 Verilog 的每一行的功能可以实现我们的目的。

启动行为仿真后,会注意到 HDL 的每条有效行的行号右侧都有一个红色圆圈。单击这些红色圆圈之一将启用该行上的断点。

2ef3a4a70cd7caa9fb42bfdcbf824aea.png

当仿真运行并遇到该断点时,可以使用顶部工具栏中的“步进”按钮(如下所示)或 F8 来逐步执行剩余的代码行。

e751868f2bf347d64b8e6cd61a243fb1.png

最后,我们看到 FIR 滤波器的新逻辑设计确实按预期运行!

6198f80a63810a81984ce1804fc5269f.png

总结

上面的两个例子能证明在目标 FPGA 芯片上,最终输出相同结果的两种不同的 HDL 编写方式对时序影响不同的重要性。这就是为什么在编写代码脑中“有电路”是很重要的原因。

一本Verilog HDL代码对应电路的书,助你快速编写可综合模型

c231f96c95faad330ab7c341d0be3608.jpeg

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos集群Raft反序列化漏洞-修复

近日&#xff0c;奇安信CERT监测到 Nacos 集群Raft反序列化漏洞(QVD-2023-13065)&#xff0c;在Nacos集群处理部分Jraft请求时&#xff0c;攻击者可以无限制使用hessian进行反序列化利用&#xff0c;最终实现代码执行。鉴于该漏洞仅影响集群间通信端口 7848(默认配置下)&#x…

计算机网络管理-实验6-使用SNMPc开展网管活动

一、实验目的 全面学习SNMPc网络管理软件业务服务监控功能&#xff0c;了解如何使用网管软件从事网络管理工作 二、实验内容与设计思想 1&#xff09;操作映射数据库。 2&#xff09;查看管理对象的MIB数据。 3&#xff09;创建、保存长期统计数据&#xff08;要求一定时长…

spring事务隔离

在数据库中读取数据时&#xff0c;可能会遇到以下三个常见的问题&#xff1a;脏读&#xff08;Dirty Read&#xff09;、不可重复读&#xff08;Non-repeatable Read&#xff09;和幻读&#xff08;Phantom Read&#xff09;。 这些问题主要涉及并发事务的隔离性和一致性。 脏…

ChatGPT 4 的 6 个最佳使用场景

作者&#xff1a;SYDNEY BUTLER 译者&#xff1a;明明如月 无论是在 ChatGPT 中还是通过 API&#xff0c;对 OpenAI 的 GPT-4 模型的访问比 GPT-3.5 限制更多。这意味着你需要慎重考虑在何种情况下使用 GPT-4&#xff0c;并选择性地将最适合的任务交给它&#xff0c;以便让其发…

来薅羊毛!阿里云 AI 神器公测了

阿里云 AI 神器「通义听悟」上线了&#xff0c;宣称是身边的 AI 学习助手。这名字听着挺玄乎的&#xff0c;老逛也去试了一下&#xff0c;确实解决了之前遇到的很多问题。 01 视频转文字 老逛是小破站的资深用户&#xff0c;喜欢几个不错的 UP 主&#xff0c;比如老蒋&#xff…

sklearn中的roc_auc_score(二分类或多分类)

官方API地址&#xff1a; sklearn.metrics.roc_auc_score — scikit-learn 1.2.2 documentationExamples using sklearn.metrics.roc_auc_score: Release Highlights for scikit-learn 0.22 Release Highlights for scikit-learn 0.22 Probability Calibration curves Probabi…

AI创作与大语言模型:2023亚马逊云科技中国峰会引领企业应用新潮流

川川出品&#xff0c;必属精品。 文章目录 CodeWhispere免费的代码生成器安装教程使用自动编码 2023亚马逊云科技中国峰会最后总结 CodeWhispere免费的代码生成器 这里我介绍亚马逊云科技的一个产品&#xff0c;那就是Amazon codewhisperer。大家肯定对AI各种产品的火爆已经有…

F/S系统分分钟系统秒变BS/CS,但共享文件夹上的DBF访问掉了个坑

接VFP MIX ALL社群狐友求助&#xff0c;说IIS访问共享文件夹的DBF出错了&#xff1a; 猫猫复现了一下错误: 错误号1705 不能访问DBF表 这个问题估计还是会有很多狐友会遇到这个问题&#xff0c;那么我们就来解决一下吧. 在服务器上面建好共享文件夹 \\newserver\dbf 里面放一个…

开源项目|EasyOCR一款实用的图片OCR文字识别项目

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; “ 花开堪折直须折&#xff0c;莫待无花空折枝。 ” 作者主页&#xff1a;[ https://www.weiyigeek.top ] 博客&…

一级建造师执业资格考试--工程经济--速学36记--联想法

第一记&#xff1a;利息的计算 第二记&#xff1a;等值计算 第三记&#xff1a;名义利率与有效利率 第四记&#xff1a;经济效果评价指标体系 第五记&#xff1a;静态投资回收期分析 第六记&#xff1a;静态投资回收期分析 第七记&#xff1a;预付款 第八记&#xff1a;施工索赔…

2023年深圳某互联网公司前端开发初级岗笔试真题(含解析和源码)

&#x1f4da;关于该专栏: 该专栏的发布内容是前端面试中笔试部分真题、答卷类、机试等等的题目&#xff0c;题目类型包括逻辑题、算法题、选择题、问答题等等&#xff0c;除了内容的分享&#xff0c;还有解析和答案。真实来自某些互联网公司&#xff0c;坐标广东广州、深圳。 …

如何使用Python Flask和MySQL创建管理用户的REST API

部分数据来源&#xff1a;ChatGPT 引言 在现代化的应用开发中&#xff0c;数据库是一个非常重要的组成部分。关系型数据库&#xff08;例如&#xff1a;MySQL、PostgreSQL&#xff09;在这方面尤其是很流行。Flask是一个Python的web框架&#xff0c;非常适合实现REST API。在…

NLP学习笔记六-lstm模型

NLP学习笔记六-lstm模型 上一篇我们讲的是simple RNN模型&#xff0c;那么其实lstm模型更像是simple RNN模型的改进或者变种。 对于lstm模型&#xff0c;我们先看下面一张图&#xff1a; 其实lstm模型的思想是建立在simple RNN模型上的&#xff0c;但是要更加贴近于显示&…

内网安全:内网渗透.(拿到内网主机最高权限 vulntarget 靶场 A)

内网安全&#xff1a;内网渗透.&#xff08;拿到内网主机最高权限&#xff09; 内网穿透又被称为NAT穿透&#xff0c;内网端口映射外网&#xff0c;在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题。通过映射端口&#xff0c;让外网的电脑找到处于内网的电脑。…

数据分析第19课pyecharts布局(基础图形绘制)

官网:https://pyecharts.org/#/zh-cn/global_options?id=legendopts%ef%bc%9a%e5%9b%be%e4%be%8b%e9%85%8d%e7%bd%ae%e9%a1%b9 不想每个属性方法的看,可以直接看gallery 官网的数据都是静态的,如果要做数据实时更新的,即做前后端结合时,会用到Vue框架,与后端连接,实现动…

Nacos架构与原理 - CAP一致性协议 ( Raft Distro)

文章目录 为什么 Nacos 需要⼀致性协议为什么 Nacos 选择了 Raft 以及 Distro从服务注册发现来看从配置管理来看为什么是 Raft 和 Distro &#xff1f;Raft (CP模式)Distro &#xff08;AP模式&#xff09; Nacos ⼀致性协议的演进早期的 Nacos ⼀致性协议当前 Nacos 的⼀致性协…

[Python图像处理] 基于离散余弦变换的安全扩频数字水印

基于离散余弦变换的安全扩频数字水印 数字水印基于离散余弦变换的安全扩频数字水印实现安全扩频数字水印相关链接 数字水印 数字水印是可见的或不可见的标识码&#xff0c;这种标识码被永久嵌入图像中&#xff0c;并且即使在解码过后后仍存在于图像中。为了保证有效性&#xf…

Jetpack Compose 中在屏幕间共享数据的 5 种方案

1. 路由传参 Jetpack Compose 中路由传参的方式有很多种&#xff0c;具体可以参考 Jetpack Compose 中的导航路由 以下是最简单的路由传参测试代码&#xff1a; import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.…

Java开发框架:Spring介绍

Spring 概述特点Spring 程序遇到的问题与解决 概述 Spring 是 Java EE 编程领域中的一个轻量级开源框架&#xff0c;由 Rod Johnson 在 2002 年最早提出并随后创建&#xff0c;目的是解决企业级编程开发中的复杂性&#xff0c;实现敏捷开发的应用型框架 。其中&#xff0c;轻量…

代理模式的运用

文章目录 一、代理模式的运用1.1 介绍1.2 结构1.3 静态代理1.3.1 火车站买票案例类图1.3.2 代码 1.4 JDK动态代理1.4.1 代码1.4.2 JDK动态代理的执行流程 1.5 CGLIB动态代理1.5.1 导包1.5.2 代码 1.6 三种代理的对比1.7 优缺点1.8 使用场景 一、代理模式的运用 1.1 介绍 由于…