为什么时序逻辑电路会落后一拍?

1、时序逻辑电路落后一拍?

FPGA初学者可能经常听到一句话:“时序逻辑电路,或者说用 <= 输出的电路会延迟(落后)一个时钟周期。”但在仿真过程中经常会发现不符合这一“定律”的现象–明明是在仿真时序逻辑,怎么输出不会落后一拍?

先来看一个简单的例子:把输入信号用时序逻辑电路寄存两次,即俗称的“打两拍”。Verilog代码如下:

module test(  
	input 				clk,	//系统时钟;  
	input				rst,	//系统复位,高电平有效;  

	input		[1:0]	in,		 
	output		[1:0]	out 
);

reg [1:0]	in_r,in_rr;		//分别打一拍、打两拍

assign out = in_rr;

always@(posedge clk or posedge rst)begin 
	if(rst)begin 
		in_r <= 2'd0;		//复位初始值
		in_rr <= 2'd0;		//复位初始值
	end
	else begin
		in_r <= in;			//输入打一拍
		in_rr <= in_r;		//输入打两拍
	end
end
	
endmodule

然后再写个TB文件来仿真一下:

`timescale 1 ns/1 ns  
module tb_test();  

//输入输出端口
reg			clk;  
reg			rst;  
reg [1:0]	in;
wire [1:0]	out; 

//例化被测试模块	
test  u_test (  
	.clk	(clk),  
	.rst	(rst),  
	.in		(in	),   
	.out	(out)  
);  

//生成系统时钟,周期10ns;  
initial begin 
	clk = 1;  
	forever #5 clk = ~clk;  
end

//生成复位信号
initial begin 
	rst = 1;
	#25;  
	rst = 0;  
end 
 
//生成输入信号(测试激励)
initial begin
	in = 0; 
	#30;
	repeat(8)begin			//循环8次;
		#10  in = in + 1; 	//输入递增1
	end  
	$stop;					//停止仿真
end

endmodule

这段测试代码的测试逻辑是:在复位完成后,每10ns依次对输入信号in执行+1操作,观察打一拍信号in_r和打两拍信号in_rr的变化。来看下仿真结果:
在这里插入图片描述

  • 输入信号in在每个时钟上升沿递增1,符合预期
  • 信号in_r原本应该落后信号in一个时钟周期,但上图中二者却完全同步,不符合预期!
  • 信号in_rr落后信号in_r一个时钟周期,符合预期

那么,是什么导致仿真结果与预期目的不符?

2、建立时间、保持时间和数据输出延迟

在FPGA设计中所用的底层时序逻辑单元是D触发器(DFF,D Flip-Flop),在理想状况下,可以认为DFF的变化是瞬态的,即输出从0到1或者从1到0,都是在一瞬间完成。但在实际使用中,这种瞬态变化显然不可能存在,所以寄存器的输出必定需要一些时间,而这个时间就是Tco。
在这里插入图片描述
上图是一张DFF的非理想状态下的数据传输示意图,为此需要明确3个概念:

  • Tsu:D端的数据必须在时钟上升沿到来之前的一定时间内就已经保持稳定,该时间被称为D触发器的建立时间(Tsu)。
  • Th:D端的数据必须在时钟上升沿到来之后的一定时间内继续保持稳定,该时间被称为D触发器的保持时间(Th)。
  • Tco:D端的数据不可能会在时钟上升沿出现的那一刻就立即更新到Q端,从时钟的上升沿到D端的数据稳定出现在Q端,也有一个时间,该时间称为寄存器的时钟到输出延迟(Tco)。

上面的概念理解两点即可:

  1. 如果不满足建立时间和保持时间要求,则DFF的输出可能会出现亚稳态,简单理解就是输出容易不正常,会出问题。
  2. 每个时钟上升沿(或下降沿)DFF都会从输入端采集数据并将其更新到输出端,但这个过程需要时间(Tco),所以数据的输出实际上会落后时钟上升沿一些时间。

3、时序逻辑电路落后一个周期的原因

接下来继续分析上面的仿真结果。
在这里插入图片描述

  • 在①处,信号in_r在发生变化,由于Tco的存在,所以in_r从0到1的时间是不会和上升沿同步的,会落后一点点。仿真波形表示的不是很明显,但也用了一个小小的斜坡来表示这一过程。
  • 在①处,信号in_r的变化会落后于上升沿,信号in_rr在①处采集到的值则仍是信号in_r未变化的值,即0。
  • 在②处,信号in_r的变化同上一个时钟①处类似。信号in_r的变化会落后于上升沿,信号in_rr在②处采集到的值则仍是信号in_r未变化的值,即1。

这样理解起来可能还是不够直观,没事,我们把代码做一些小小的改变:

in_r <= #1 in;			//输入打一拍
in_rr <= #1 in_r;		//输入打两拍

只是把输出语句加一个 “#1”,即输出会延迟1ns。聪明的你应该已经看出来了,这就是用来模拟Tco这个概念的。

在这里插入图片描述

继续看仿真结果,是不是一目了然?

  • 在①处,由于Tco的存在,所以in_rr采集到的in_r的值是0,所以in_rr输出也是0,而且输出会落后一个Tco时间(但是由于值相同,所以看不出来)
  • 在②处,由于Tco的存在,所以in_rr采集到的in_r的值是1,所以in_rr输出也是1,而且输出也会落后一个Tco时间

所以现在我们清楚了,时序逻辑电路的输出根本就不会落后一个时钟周期,而只会落后一个Tco时间。二者之所以看上去会落后一个周期,完全是由于前级输出的Tco时间存在,导致后级电路在当前时钟上升沿无法采集到最新值,而只能采集到前级未变化的值!

4、为什么把时序逻辑仿成了组合逻辑?

上面的仿真还有个问题悬而未决,那就是in_r是in被寄存后的信号,为啥没有落后in一个时钟周期?问题出在仿真机制和TB文件中对in的赋值方式上。

在TB中,我们是这么对in赋值的:

#10 in = in + 1; //输入递增1

注意看,用的是阻塞赋值“ = ”,阻塞赋值“ = ”一般用来描述组合逻辑,而非阻塞赋值“ <= "则一般用来描述时序逻辑。

虽然输入信号in是我们构建的一个虚拟向量,但对于被测试模块来说,这个激励仍然被视作是来自于上级模块的输出,所以需要指明它到底是一个组合逻辑的输出值还是一个时序逻辑的输出值。

如果它是用“ = ”来描述的,那它就是来自组合逻辑,而组合逻辑的输出是不与时钟上升沿有关的,它的输出几乎就是瞬时完成的。如果它是用“ <= ”来描述的,那它就是来自时序逻辑,而时序逻辑的输出则会落后时钟上升沿一个Tco时间。

回到上面的仿真结果,由于信号in使用“ = ”来赋值,所以它的每一次更新都几乎与时钟上升沿同步,并不会有Tco时间的存在,每一个上升沿后级的in_r信号都能采集到最新的in值,所以二者并不会有一个周期的延迟。

假如我们把信号in改成“ <= ”这种赋值方式:

#10 in <= in + 1; //输入递增1

那么仿真结果就是这样了:
在这里插入图片描述

这与我们最初料想的一致:打一拍信号in_r落后输入信号in一个时钟周期,打两拍信号in_rr后输入信号两个时钟周期。

如果说还有问题的话,就是输入信号in的变化没有很好的体现Tco时间,所以再修改一下:

#10 in <= #1 in + 1; //输入递增1

在这里插入图片描述

嗯,这样就没问题了。由于采用了“#1”这种赋值方式来模拟Tco的存在,所以你应该再也不会搞错信号在哪个时钟沿采样和哪个时钟沿变化了。

5、总结

  • 时序逻辑电路的输出不是瞬时发生的,而是需要一定的时间,这个时间就是Tco
  • 时序逻辑电路并没有真正意义上的落后一拍,落后一拍的原因是因为Tco的存在,导致在当前时钟上升沿无法采集到最新的值,而只能采集到未变化的值
  • 在仿真时,输入信号尽量用非阻塞赋值“<=”来模拟其来自寄存器的输出,这样的仿真结果更接近实际电路
  • 可以采用“#1”这种赋值方式来模拟Tco的存在,这可以在仿真时带来很大的便利

  • 📣您有任何问题,都可以在评论区和我交流📃!
  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐

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

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

相关文章

netstat引发系统负载升高故障案例一则

关键词 linux、centoscpu load、netstat、strace阻塞、卡顿 There are many things that can not be broken&#xff01; 如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; 在一次线上业务的阻塞故障中&#xff0c;发现罪魁祸首是执行了大量netstat的命令…

使用宝塔面板部署Node.js+Mysql服务和Vue3-Admin项目到云服务器上

准备工作 一台云服务器&#xff0c;可以先用免费试用一个月的服务器进行练手&#xff1b;我这里选择的是腾讯云的轻量云服务器&#xff1b; 1、在云服务器上安装宝塔面板 宝塔面板官网地址&#xff1a;https://www.kancloud.cn/chudong/bt2017/424209 1.1 安装Xshell脚本工…

开源CRM客户管理系统-FeelCRM

FeelCRM客户管理系统 开源项目介绍 FeelCRM客户管理系统&#xff0c;符合中小企业业务流程&#xff1b;支持线索管理、客户管理、商机管理、合同管理、审核管理等多个模块&#xff1b;希望能为广大中小企业以及开发者们提供一个更多的可能性&#xff1b;本版本是我公司跨语言…

C#,打印漂亮杨辉三角形(帕斯卡三角形)的源代码

杨辉 Blaise Pascal 这是某些程序员看完会哭的代码。 杨辉三角形&#xff08;Yanghui Triangle&#xff09;&#xff0c;是一种序列数值的三角形几何排列&#xff0c;最早出现于南宋数学家杨辉1261年所著的《详解九章算法》一书。 欧洲学者&#xff0c;最先由帕斯卡&#x…

Windows10上使Git Bash支持rsync命令操作步骤

rsync命令是linux上常用的工具之一&#xff0c;用于远程以及本地系统中拷贝/同步文件和文件夹。 Windows Git Bash默认并不支持rsync&#xff0c;如下图所示&#xff1a; 使Git Bash支持rsync命令操作步骤&#xff1a; 1.从https://repo.msys2.org/msys/x86_64/ 下…

1.26寒假集训

A: 解题思路&#xff1a; 只有一行一列的时候输出1&#xff0c;多列就输出2 有多行多列的时候&#xff0c;输出4 下面是c代码&#xff1a; #include<iostream> using namespace std; int main() {long long n,m,t;cin >> t;while(t ! 0){cin >> n >&g…

java安装,从java1.8升级到java11.0,java,javac,javaw,javaws,jdk,jre

最近在学习 PyFlink&#xff0c;需要安装Java11环境&#xff0c;但是本机已经安装了java1.8&#xff0c;在升级的过程中遇到了一些问题&#xff0c;在这里记录一下。 windows下安装JDK11 下载JDK11&#xff1a;https://www.oracle.com/java/technologies/downloads/#java11-w…

【SVD生成视频+可本地部署】ComfyUI使用(二)——使用Stable Video Diffusion生成视频 (2023.11开源)

SVD官方主页 &#xff1a; Huggingface | | Stability.ai || 论文地址 huggingface在线运行demo : https://huggingface.co/spaces/multimodalart/stable-video-diffusion SVD开源代码&#xff1a;Github&#xff08;含其他项目&#xff09; || Huggingface 在Comfyui使用&…

[bat]基于msg的弹窗提示

一、方案 1、定时自动消失的弹窗 代码&#xff1a; echo off echo method 1 msg * /time:5 "123456" REM echo method 2 REM msg * "123456"pause 效果&#xff1a; 立即弹窗在5秒后消失。 2、一直存在的弹窗 源码&#xff1a; echo off REM echo m…

方法重载与方法重写差别

写在开头 请聊一聊Java中方法的重写和重载&#xff1f; 这个问题应该是各大厂面试时问的最多的话题之一了&#xff0c;它们几乎贯穿了我们日常的开发工作&#xff0c;在过往的博客中我们多多少少都提到过重载与重写&#xff0c;而今天我们就一起来详细的学习一下这二者的功能与…

【Elsevier】中科院2区SCI,仅3个月录用!接收领域广!

关注公主号【SciencePub学术】&#xff0c;发现期刊更多精彩~ 1 数据处理类SCIE&#xff08;高质量&#xff09; 【期刊简介】IF&#xff1a;6.5-7.0&#xff0c;JCR1区&#xff0c;中科院2区&#xff1b; 【出版社】Elsevier出版社 【版面情况】正刊&#xff0c;仅5篇版面…

jetson-inference----docker内运行分类任务

系列文章目录 jetson-inference入门 jetson-inference----docker内运行分类任务 文章目录 系列文章目录前言一、进入jetson-inference的docker二、分类任务总结 前言 继jetson-inference入门 一、进入jetson-inference的docker 官方运行命令 进入jetson-inference的docker d…

Github 2024-01-26 开源项目日报Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-26统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4Jupyter Notebook项目2HTML项目1Shell项目1Dockerfile项目1非开发语言项目1Go项目1Rust项目1 高级…

最小覆盖子串(Leetcode76)

例题&#xff1a; 分析: 比如现在有字符串&#xff08;s&#xff09;&#xff0c;s "ADOBECODEBANC", 给出目标字符串 t "ABC", 题目就是要从原始字符串&#xff08;s&#xff09;中找到一个子串&#xff08;res&#xff09;可以覆盖目标字符串 t &…

vue3预览pdf文件的几种方法

vue3预览pdf集中方法 方法一&#xff1a; iframe&#xff1a;这种方法显示有点丑 <iframesrc"E:\\1.pdf"frameborder"0"style"width: 80%; height: 100vh; margin: auto; display: block"></iframe>方法二&#xff1a; 展示效果&…

【C++】wxWidgets编程的程序入口点

在wxWidgets中&#xff0c;程序的入口点通过wxIMPLEMENT_APP宏定义来设置&#xff0c;该宏会扩展为一个实现了main函数或者在Windows上是WinMain函数的代码。wxIMPLEMENT_APP宏与wxDECLARE_APP宏一起使用来设置基于wxWidgets的应用程序的启动代码。 使用wxIMPLEMENT_APP宏通常是…

【AI Agent系列】【MetaGPT】9. 一句话订阅专属信息 - 订阅智能体进阶,实现一个更通用的订阅智能体(2)

文章目录 0. 前置推荐阅读和本文内容0.1 前置推荐阅读0.2 本文内容 1. 修改一&#xff1a;直接用大模型获取网页信息&#xff0c;不用爬虫程序1.1 我们要给大模型什么内容1.2 提取网页文本信息1.3 组织Action1.4 完整代码及细节注释1.5 可能存在的问题及思考 2. 修改二&#xf…

实体识别与分类方法综述

目录 前言1 实体识别简介2 基于模板和规则的方法3 基于序列标注的方法3.1 常见序列标注模型3.2 模型参数估计和学习问题3.3 常见序列预测模型 4. 基于深度学习的实体识别方法5 基于预训练语言模型的实体识别5.1 BERT、GPT等预训练语言模型5.2 解码策略 6 特殊问题与挑战6.1 标签…

视频渲染靠cpu还是显卡 会声会影视频渲染的作用是什么

视频渲染最占用的资源就是CPU&#xff0c;多核心多线程&#xff0c;这样才能渲染快。渲染可以在时间线上实时平滑预览&#xff0c;便于编辑&#xff0c;最终导出成片的时候速度也会快一些&#xff0c;渲染就是对每桢的图像进行重新优化的过程。 渲染的作用主要是能够保证使用者…

C#使用RabbitMQ-2_详解工作队列模式

简介 &#x1f340;RabbitMQ中的工作队列模式是指将任务分配给多个消费者并行处理。在工作队列模式中&#xff0c;生产者将任务发送到RabbitMQ交换器&#xff0c;然后交换器将任务路由到一个或多个队列。消费者从队列中获取任务并进行处理。处理完成后&#xff0c;消费者可以向…