【必看】时序逻辑仿真成组合逻辑?你知道原因吗?

news2024/11/26 21:31:22

  对于初学者,一般会遇到这种情况,明明写的时序逻辑,结果仿真结果却是组合逻辑,然后看遍设计代码,始终找不到原因,交流群、知乎这种问题随处可见。但不要怀疑软件问题,modelsim这些专用软件基本不会遇见软件自身问题,原因其实很简单,因为多数人只关注设计文件不会关注TestBentch的合理性,导致找不到问题原因,后文分析原因并给出避免这种问题​的方法。

  在仿真时经常会使用“#”和“@(posedge clk)”来实现延迟,“#”后面跟数字,表示延迟数字对应最小的时间单位,而“@(posedge clk)”则用来检测clk信号上升沿,如果CYCLE表示始终周期对应的时间长度,那么“#(CYCLE)”表示延迟一个时钟周期长度的时间。在时钟上升沿输出数据后,使用“@(posedge clk)”,会延迟到下个时钟上升沿,同样也可以表示延迟一个时钟周期,那有没有区别?

  分析以下代码,输出dout就是两个输入dataa与datab相加,由于是时序逻辑,dout会延迟dataa或datab变化后的一个时钟周期。

module add(  
   input                clk     ,//系统时钟;  
   input                rst_n   ,//系统复位,低电平有效;  

   input       [3 : 0]  data_a  ,//加数dataa;  
   input       [3 : 0]  data_b  ,//加数datab;  
   output reg  [4 : 0]  dout      
   );
   always@(posedge clk or negedge rst_n)begin  
      if(rst_n==1'b0)begin//初始值为0;  
         dout <= 4'd0;  
      end  
      else begin  
         dout <= data_a + data_b;  
      end  
   end

endmodule

  Testbench如下所示:

`timescale 1 ns/1 ns  
module test();  
   parameter   CYCLE       =  10    ;//系统时钟周期,单位ns,默认10ns;  
   parameter   RST_TIME    =  5     ;//系统复位持续时间;  
   parameter   STOP_TIME   =  100   ;//仿真运行时间,复位完成后运行100个系统时钟后停止;  

   reg                        clk   ;//系统时钟,默认100MHz;  
   reg                        rst_n ;//系统复位,默认低电平有效;  
   reg         [3 : 0]        data_a;  
	reg         [3 : 0]        data_b;
	wire        [4 : 0]        dout  ;  
	
   add  u_add (  
      .clk        ( clk       ),  
      .rst_n      ( rst_n     ),  
      .data_a     ( data_a    ),  
      .data_b     ( data_b    ),  
      .dout       ( dout      )  
   );  
	//生成周期为CYCLE数值的系统时钟;  
	initial begin  
      clk = 1;  
      forever #(CYCLE/2) clk=~clk;  
	end
   //生成复位信号;  
   initial begin  
      rst_n = 1;  
      #2;  
      rst_n = 0;//开始时复位10个时钟;  
      #(RST_TIME*CYCLE);  
      rst_n = 1;  
   end  
   //生成输入信号din;  
   initial begin  
      data_a = 0;data_b=0;//输入数据初始化为0;  
      #(10*CYCLE);//延迟10个时钟周期;  
      repeat(STOP_TIME)begin//循环STOP_TIME次;
         #(CYCLE);  
         data_a = {$random} % 16;  
         data_b = {$random} % 16;  
      end  
      $stop;//停止仿真;  
   end

endmodule

  使用modelsim仿真如下图所示,奇怪的是为什么时序逻辑仿真成组合逻辑了?
在这里插入图片描述

图1 使用#延迟仿真结果

  分析:加法器代码肯定是没有问题的,modelsim软件也是经过fpga设计以及IC设计人员多年使用,是最常用的仿真工具,也不可能出现这样的低级bug。如果你把代码下载到开发板上,使用在线逻辑分析仪抓取数据,能够得到正确的运行结果,但是仿真就是错误的。这是为什么?那就只剩下写的testbench文件了,来看下testbench与输出相关的信号,首先时钟和复位信号是没有问题的,那就只剩下dataa与datab的产生模块了,如下所示:

//生成输入信号din;  
initial begin  
   data_a = 0;data_b=0;//输入数据初始化为0;  
   #(10*CYCLE);//延迟10个时钟周期;  
   repeat(STOP_TIME)begin//循环STOP_TIME次;
      #(CYCLE);  
      data_a = {$random} % 16;  
      data_b = {$random} % 16;  
   end  
      $stop;//停止仿真;  
end

  开始仿真时两个输入信号都被赋值为0,经10个时钟延迟之后进入forever循环内,每次循环之前都会把数据延迟一个时钟周期,然后在对两个输入信号赋一个0~15的随机值。逻辑上其实没有问题,但是注意一个问题,每次给dataa和datab赋值时间与时钟clk上升沿是对齐的,导致D触发器的输入信号在时钟上升沿时发生变化,由此导致D触发器数据采集错误,最终导致D触发器输出信号dout提前更新数据。这里实际上与保持时间违例有点类似,D触发器的下一个输入数据来得过快,影响了上一个数据的采集。

  解决方法很简单,因为是数据刚好在时钟上升沿时发生更新导致D触发器数据采集错误,那么把两个输入数据全部延迟一点不就行了,修改如下,将两个输入数据的所有变化均延迟1ns,与时钟上升沿错开。

//生成输入信号din;  
initial begin  
   #1;  
   data_a = 0;data_b=0;//输入数据初始化为0;  
   #(10*CYCLE);//延迟10个时钟周期;  
   repeat(STOP_TIME)begin//循环STOP_TIME次;  
      #(CYCLE);  
      data_a = {$random} % 16;  
      data_b = {$random} % 16;  
      end  
      $stop;//停止仿真;  
end

  修改后仿真结果如下:
在这里插入图片描述

图2 添加#1的仿真结果

  从上面仿真结果就可以看到,dataa与datab的变化都延迟时钟上升沿1ns之后,就没有在影响仿真结果了。这也是为什么很多代码在对信号赋值之前会延迟1ns的原因,就是为了数据变化与时钟上升沿错开,避免发生上面这种由于testbench书写问题所引发的离奇结果。

  使用“#”会引发上面问题,那如果过使用“@(posedge clk)”这种写法还会出现那样的仿真结果?

  先给答案,不会出现类似问题,因为“@(posedge clk)”表示已经检测到时钟上升沿了,那么在这之后更新的数据自然与时钟上升沿就是错开的了。

  同样的案例,只是把dataa和datab赋值的部分改成如下代码。

//生成输入信号din;  
initial begin  
   data_a = 0;data_b=0;//输入数据初始化为0;  
   #(10*CYCLE);//延迟10个时钟周期;  
   repeat(STOP_TIME) @(posedge clk)begin//循环STOP_TIME次;  
      data_a = {$random} % 16;  
      data_b = {$random} % 16;  
   end  
   $stop;//停止仿真;  
end

  上面代码表达结果与下面代码一致。

//生成输入信号din;  
initial begin  
   data_a = 0;data_b=0;//输入数据初始化为0;  
   #(10*CYCLE);//延迟10个时钟周期;  
   repeat(STOP_TIME)begin//循环STOP_TIME次;  
      @(posedge clk);  
      data_a = {$random} % 16;  
      data_b = {$random} % 16;  
   end  
      $stop;//停止仿真;  
end

  仿真结果如下,没有出现任何问题,数据变化近似与时钟上升沿对齐,但是输出数据dout没有受到影响,这就是“@(posedge clk)”的效果。
在这里插入图片描述

图3 使用@(posedge clk)的仿真结果

  “#”和“@(posedge clk)”虽然都可以写成延时几个时钟周期的形式,但是他们是有区别的,区别在与“#”延迟与时钟其实没有关系,就有可能和时钟上升沿重合,这是使用是需要注意的,建议在仿真开始时就对数据延迟1ns然后在赋值,与时钟信号变化错位。而“@(posedge clk)”本质是检测时钟上升沿,在时钟上升沿之后才会去执行后面的语句,所以数据变化与时钟上升沿变化是错位的,不会出现“#”那种问题。

  上述从效果看肯定是使用“@(posedge clk)”,作为延迟更好,使用“#(CYCLE)”延迟一个时钟更号理解,但是要注意可能遇到的问题,一般使用模板时,赋值语句开头会带有“#1;”,或者在每次赋值前用“@(posedge clk)”作为延迟,如下我常用赋值模板。

//生成输入信号din;  
initial begin  
   #1;din = 0;//输入数据初始化为0;  
   #(10*CYCLE);//延迟10个时钟周期;  
   
end

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

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

相关文章

【100天精通python】Day33:使用python操作数据库_SQLite数据库的使用与实战

目录 专栏导读 1 SQLite 简介 2 SQLite数据库安装及使用 2.1 检查 SQLite 支持 2.2 创建数据库文件 2.2.1 使用 Python 创建数据库文件&#xff1a; 2.2.2 使用命令行创建数据库文件&#xff1a; 2.3 连接到 SQLite 数据库&#xff1a; 3 SQLite 中常用的SQL语句…

WebRTC音视频通话-实现iOS端调用ossrs视频通话服务

WebRTC音视频通话-实现iOS端调用ossrs视频通话服务 之前搭建ossrs服务&#xff0c;可以查看&#xff1a;https://blog.csdn.net/gloryFlow/article/details/132257196 这里iOS端使用GoogleWebRTC联调ossrs实现视频通话功能。 一、iOS端调用ossrs视频通话效果图 iOS端端效果图…

HTML详解连载(7)

HTML详解连载&#xff08;7&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽结构伪类选择器作用 :nth-child&#xff08;公式&#xff09;作用举例 伪元素选择器作用注意&#xff1a; PxCoook作用盒子模型-重要组成部分 盒子模型-边框线属性名属性…

Java-运算符和控制语句(上)(基于c语言的补充)

算术运算符 关于求余 不管分子&#xff0c;分母是正还是负&#xff0c;对于分母&#xff0c;直接取正&#xff1b;对于分子若有负号&#xff0c;则先提取出来&#xff1b;剩下两个正的分子分母运算&#xff1b;最后&#xff0c;若刚才的分子有负号&#xff0c;对最后的结果添加…

fork:创建一个子进程

函数原型&#xff1a;pid_t fork&#xff08;void&#xff09;&#xff1b; 返回值&#xff1a; 成功&#xff1a;返回子进程id给附近父进程&#xff0c;返回0给子进程 失败&#xff1a;返回-1&#xff0c;并且设置错误号&#xff0c;同时子进程不会被创建 注意&#xff1…

C++ QT(一)

目录 初识QtQt 是什么Qt 能做什么Qt/C与QML 如何选择Qt 版本Windows 下安装QtLinux 下安装Qt安装Qt配置Qt Creator 输入中文配置Ubuntu 中文环境配置中文输入法 Qt Creator 简单使用Qt Creator 界面组成Qt Creator 设置 第一个Qt 程序新建一个项目项目文件介绍项目文件*.pro样式…

锁与原子操作的底层原理

偏向锁 在一个系统当中&#xff0c;大部分时间都不存在并发问题&#xff0c;但频繁的加锁释放锁又会占用大量系统资源。因此为了让线程获得锁的代价更低而引入了偏向锁。 获得偏向锁 1&#xff09;检查该锁是否被当前线程持有 2&#xff09;通过CAS操作修改对象头 3&#…

python环境下载安装教程,python运行环境怎么下载

本篇文章给大家谈谈python安装步骤以及环境变量配置&#xff0c;以及下载python需要设置环境变量吗&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 1.https://www.python.org/downloads/windows/ 下载适合自己电脑的python安装包 2.下载后安装即可 3.配置环…

Vue中如何更好地封装组件?

子组件接受父组件传递的事件 1.子组件使用事件名"$emit(父组件中传递的事件名,想给父组件传递的参数(可选))" click"$emit(click)" 2.子组件使用 v-on"$listeners" 父组件&#xff1a; <template><div id"app"><myCo…

Ceph分布式存储系统优化分析

Ceph支持多种存储访问接口&#xff0c;现有的多种性能测试工具都可用于Ceph的性能测试&#xff0c;如测试块接口性能的fio&#xff0c;iometer等&#xff1b;测试CephFS接口的filebench&#xff0c;fio等;测试对象接口的cosbench等。Ceph有专用的基准测试集CBT&#xff0c;其包…

并查集的原理与实现

1.概念 2.生活中的例子 小弟-老大&#xff1b; 帮派识别 3.实现 3.1 初始化 3.2 中间过程 3.3合并 3.4 并查集路径优化 直接把下面的节点指向最终的老大。 3.5 伪代码实现 3.6JAVA实现 findRoot: 谁是帮派的老大。例如山鸡的老大是陈浩南 connected: 我们是不是同一个大…

【机器学习 | 数据预处理】 提升模型性能,优化特征表达:数据标准化和归一化的数值处理技巧探析

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

勇敢牛牛,爱吃青草

牛顿问题&#xff08;牛吃草问题 / 消长问题&#xff09; 牛顿问题&#xff08;牛吃草问题/消长问题&#xff09; 牛顿问题&#xff08;牛吃草问题/消长问题&#xff09; 牧场上有一片青草&#xff0c;每天都生长得一样快。这片青草供给 10 头牛吃&#xff0c;可以吃 22 天&…

元宇宙时代超高清视音频技术白皮书关于流媒体协议和媒体传输解读

流媒体协议 元宇宙业务场景对流媒体传输的实时性和互动性提出了更高的要求&#xff0c;这就需要在传统的 RTMP、SRT、 HLS 等基础上增加实时互动的支持。实时互动&#xff0c;指在远程条件下沟通、协作&#xff0c;可随时随地接入、实时地传递虚实融合的多维信息&#xff0c;身…

LangChain-ChatGLM在WIndows10下的部署

LangChain-ChatGLM在WIndows10下的部署 参考资料 1、LangChain ChatGLM2-6B 搭建个人专属知识库中的LangChain ChatGLM2-6B 构建知识库这一节&#xff1a;基本的逻辑和步骤是对的&#xff0c;但要根据Windows和现状做很多调整。 2、没有动过model_config.py中的“LORA_MOD…

Idea中使用statement接口对象,显示mysql版本号,所有库和表名

使用statement 接口对象&#xff0c;进行以下操作&#xff1a; 显示数据库版本号显示所有库显示所有库中的table表 显示数据库版本号&#xff1a; public class StatementDemo {Testvoid showall(){try{Statement st conn.createStatement();ResultSet rs st.executeQuery(…

C++_模板初阶

在面向对象中&#xff0c;我们可以使用重载来实现多态。 但是问题在于&#xff0c;重载的函数仅仅是类型不同&#xff0c;代码复用率比较低&#xff0c;只要有新的类型出现时&#xff0c;就要增加对应的函数&#xff1b;另一方面它的代码可维护性比较低&#xff0c;一个出错可…

python递归实现逆序输出数字

一、问题描述 编程实现将输入的整数逆序输出 二、问题分析 逆序输出数字实际是一个数值问题的递归 三、算法设计 该问题要求输入任意一个整数&#xff0c;实现它的逆序输出。首先判断输入的整数是正整数还是负整数&#xff0c;如果是负整数&#xff0c; 则在逆序输出前应先…

1059 Prime Factors

个人学习记录&#xff0c;代码难免不尽人意。 Sample Input: 97532468 Sample Output: 975324682^211171011291 下面是我第一次自己写的错误代码&#xff0c;虽然测试点都通过了&#xff0c;但是是因为测试样例有限的原因。 #include<cstdio> #include<iostream> #…

防御第九次作业

一、根据以下问题总结当天内容 1. SSL工作过程是什么&#xff1f; 当客户端向一个 https 网站发起请求时&#xff0c;服务器会将 SSL 证书发送给客户端进行校验&#xff0c;SSL 证书中包含一个公钥。校验成功后&#xff0c;客户端会生成一个随机串&#xff0c;并使用受访网站的…