Verilog使用always块实现时序逻辑

news2024/10/6 22:29:34

这篇文章将讨论 verilog 中一个重要的结构---- always 块(always block)

verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反,时序电路电路使用时钟并一定需要触发器等存储元件。因此,输出信号与时钟同步,而不是立即发生变化。

在verilog中需要使用 always 块来编写时序逻辑电路,这一点至关重要。

1、Verilog 中的 Always 块(Always Block

在编写 verilog 时,可以使用过程块(procedural blocks)来创建顺序执行的语句,过程块对于实现时序电路特别重要。相反,连续赋值语句在设计中并发(即并行)执行,这与底层电路的性质相匹配----底层电路由许多独立的逻辑门组成。

always 块是 verilog 中最常用的过程块之一,每当敏感列表中的一个信号改变状态时,always 块中的所有语句都会按顺序执行。

下面的 verilog 代码显示了 always 块的一般语法。

always @(<sensitivity_list>) begin
     //这里写要实现的代码
end

使用这个结构时需要小心,因为有一些 verilog 独有的特性,特别是初学者经常很难理解信号在 always 块中更新的方式。

在使用 always 块时,可以并行或顺序(串行)更新信号的值。这取决于使用的是阻塞赋值(blocking assignment)还是非阻塞赋值(non-blocking assignment)。要想成为一名高效的 verilog 设计者,就必须很好地理解 always 块。

1.1、敏感列表(Sensitivity Lists

在 always 块中编写的任何代码都会连续运行,这意味着代码块中的语句会按顺序执行,直到最后一行。一旦执行完序列中的最后一行,程序就会循环回到第一行,然后,always 块中的所有语句将再次按顺序执行。

然而,这种行为并不能描述这样一种电路----其中一个输入信号改变状态之前将保持稳定状态的真实电路。verilog使用 alway 块中的敏感列表来模拟这种行为,所以,always 块中的代码将仅在敏感列表中的信号之一更改状态后执行。

1.1.1、触发器(Flip Flop )示例

与所有触发器一样,D 触发器的输出仅在时钟上升沿时才改变状态,因此可以将时钟信号包含在敏感列表中,以便 always 块仅在时钟信号出现上升沿时执行。

下面的 verilog 代码展示了如何使用 always 块实现 D 触发器。

always @(posedge clock) begin
  q <= d;
end

在此代码示例中使用了 posedge 来确定何时存在从 0 到 1 的转换(即时钟上升沿)。

当posedge的计算结果为真时,将执行 always 块中的单行代码,这行代码将输入D 的值分配给输出 Q。在 verilog 中使用 posedge 时,所有其他的状态变化都会被忽略,这正符合对 D 触发器的设计期望。

Verilog 也有一个具有相反功能的 negedge 。当使用negedge时,只要时钟从 1 变为 0(即时钟下降沿),always 块就会被执行。

设计者也可以不使用posedge/negedge,在这种情况下,只要敏感列表中的信号改变状态,代码就会执行。

1.1.2、敏感列表中的多个信号

在某些情况下,设计者希望在敏感列表中包含多个信号。一个常见的例子是要编写代码来实现一个具有异步复位的触发器,在这种情况下,设计师希望触发器在复位或时钟信号改变状态时才执行操作。

为此,可以在敏感度列表中列出两个信号,并用逗号分隔它们。下面的代码片段展示了如何实现这样一个触发器。

always @(posedge clock, posedge reset) begin
  if (reset) begin 
    q <= 1'b0;
  end
  else begin
    q <= d;
  end
end

由于此示例使用了高电平有效复位,高电平有效复位意味着复位仅在等于 1 时有效,因此再次在灵敏度列表中使用了 posedge ,然后使用了 if 语句的结构来确定 always 块是由复位信号还是时钟边沿信号触发。

使用Verilog-1995 标准的代码时,必须使用 or 关键字或逗号来分隔敏感列表中的信号。

下面的代码片段展示了如何使用 Verilog-1995 标准来实现异步可复位触发器。

always @(posedge clock or posedge reset) begin
  if (reset) begin 
    q <= 1'b0;
  end
  else begin
    q <= d;
  end
end

2、Verilog 中的阻塞赋值(Blocking Assignment)和非阻塞赋值(Non-Blocking Assignment

到目前为止本文使用了两种不同类型的赋值运算符。这是因为 verilog 有两种不同类型的赋值——阻塞赋值非阻塞赋值。使用非阻塞赋值编写代码时,使用 <= 符号,而阻塞赋值则使用 = 符号。

在verilog中使用连续赋值语句时时,只能使用阻塞赋值。但是,在过程块中可以使用这两种类型的赋值。

阻塞赋值通常会生成组合逻辑电路,而非阻塞赋值则通常会生成时序逻辑电路。

在 verilog 中使用阻塞赋值来对信号赋值时,信号会在代码行执行后立即更新它们的值,所以这种类型的赋值在 verilog 中通常被用来编写组合逻辑;相反,使用非阻塞赋值的信号在赋值后不会立即更新。

2.1、scheduled assignment

使用非阻塞赋值编写 verilog 代码时,代码仍然按顺序执行。但是,信号却不会以这种方式更新。为了说明为什么会这样,将以下面的扭环计数器电路(twisted ring counter)为例。

always @(posedge clock) begin
  q_dff1 <= ~q_dff2;
  q_dff2 <= q_dff1;
end

首先来看看信号立即更新时的行为。假设当时钟边沿出现时两个触发器的输出都是 0,那么代码中的第二行会将 DFF1 的输出设置为 1,然后可以看到紧接其下方的代码行会将 DFF2 的输出设置为 1。但这显然不是该电路的预期行为。

为了克服这个问题,非阻塞赋值就会做scheduled assignment(预设赋值,这个术语我也不会翻译,大概意思是赋值的发生会“ 有计划性地安排在未来的某一个时间”)。因此,信号的更改不会在赋值后立即发生,而是在将来的某个时间发生。通常,信号会在仿真周期末尾更新它们的值----这是指仿真工具在给定时间步长内执行所有代码所花费的时间。

为了更好地演示scheduled assignment的工作方式,请再次考虑简单的双触发器电路(dual flip flop circuit)。

当检测到上升沿时,模拟器首先执行更新 DFF1 的语句,然后将计划对 DFF1 的输出进行更新。当模拟器运行第二行代码,这次使用 DFF1 触发器的原始值并安排 DFF2 的更新。

由于此设计中只有两个语句,因此仿真周期现已完成。此时,所有计划的更改都将被实现并更新两个触发器的值。

2.2、综合案例(Synthesis Example

为了进一步展示 verilog 中阻塞赋值和非阻塞赋值之间的区别,接下来将再次模拟一个基本的双触发器扭环计数器电路。下面的代码片段展示了如何实现该电路。

always @(posedge clock) begin
  q_dff1 <= ~q_dff2;
  q_dff2 <= q_dff1;
end

可以看下vivado所生成的电路图,如下所示。电路中有两个触发器,而非门是则使用 LUT1 实现的。

接着来看看如果在代码中使用阻塞赋值将会得到何种电路。下面的 verilog 代码展示如何尝试使用阻塞赋值来实现该电路(错误的示范)。

always @(posedge clock) begin
  q_dff1 = ~q_dff2;
  q_dff2 = q_dff1;
end

这导致综合后的电路如下所示。

从这里可以看出,使用阻塞赋值导致电路中的第二个触发器被移除了。这样做的原因应该是相当明显的。由于 q_dff2 的值立即赋给与 q_dff1 相同的值,所以该信号路径中不应该有触发器。

这个例子实际上展示了 verilog 中阻塞赋值和非阻塞赋值之间最重要的一个区别----使用非阻塞赋值时,综合工具总是会在电路中放置一个触发器。这意味着设计者只能使用非阻塞赋值来实现时序逻辑电路。相反,设计者可以使用阻塞来创建时序电路或组合电路。

但是,设计者应该只使用阻塞赋值来实现 verilog 中的组合逻辑电路,这样做的主要原因是编写的代码将更容易理解和维护。

3、Always 块中的组合逻辑

到目前为止,本文只考虑了使用 always 块的时序电路建模。虽然这是最常见的用例,但设计者也可以使用这种方法对组合逻辑进行建模。

例如,下面的代码展示了如何使用 always 块来实现如下所示的 AND-OR 电路。

// verilog-2001标准
always @(a, b, c) begin
  logic_out = (a & b) | c;
end
 

// verilog-1995标准
always @(a or b or c) begin
  logic_out = (a & b) | c;
end

这段代码几乎与在连续赋值语句中实现的方法不同,主要区别就是被其封装在了一个 always 块中。此外还从语句中删除了assign关键字,因为在此情况下已经不再需要它了。

从这个例子中还可以看出,组合逻辑电路的敏感列表比时序逻辑电路更复杂。在实现组合逻辑电路时,实际上有两种方法可以用来编写敏感类别。

第一种方法是列出电路的每个输入,用 or 关键字或逗号分隔,这也是上面的示例代码中所使用的方法。

第二种方法是使用 * 字符来告诉综合工具自动决定将哪些信号包含在敏感列表当中。这种技术更可取,因为它更易于维护,但是,此方法是作为verilog-2001标准的一部分引入的,这意味着它不能与 verilog-1995标准的代码一起使用。

下面的代码片段展示了如何使用这两种方法。

// 穷举出所有信号的敏感列表
always @ (a, b, c)

// 使用 * 实现的敏感列表
always @ (*)

一般来讲,只在少数情况下使用 always 块对组合逻辑电路进行建模,因为它可以简化复杂组合逻辑的建模。

多路选择器(Multiplexors)

如果想要实现多路选择器,使用 always 块来实现这种组合逻辑可能是一个很有用的办法。在这种情况下,可以使用被称为 case 语句的结构来实现多路选择器。这是一种更简单、更直观的大型多路选择器的实现方法。

下面的代码片段展示了如何使用 case 语句来实现一个简单的4选1多路选择器。

always @(*)
  case (addr) begin
    0 : begin
      mux_out = a;    //当addr = 0时,执行这条语句
    end 
    1 : begin
        mux_out = b;  //当addr = 1时,执行这条语句
    end
    2 : begin
      mux_out = c;    //当addr = 2时,执行这条语句
    end
    3 : begin
      mux_out = d;    //当addr = 3时,执行这条语句
    end
  endcase
end

case 语句很容易理解,因为它通过一个变量来选择要执行哪条分支语句。在case语句中,设计者可以包含尽可能多的不同分支。此外,应该使用默认(default )分支来实现那些未被列出来的case条件值。

为了将其用作多路选择器,变量将被用作地址引脚,然后可以根据正在执行的分支将对应的值赋给多路选择器的输出。


  • 📣您有任何问题,都可以在评论区和我交流📃

  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net

  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏


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

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

相关文章

用Chrome浏览器加入新必应候补名单,微软最终还是无法赢得一切(~ ̄▽ ̄)~

前言 ChatGPT最近太火了&#xff0c;国内厂商刚开始宣布跟进时&#xff0c;微软必应搜索直接接入了ChatGPT的能力&#xff0c;推出了新必应。此举给Google当头一棒&#xff0c;相比于传统搜索引擎&#xff0c;新必应的这种搜索方式&#xff0c;让用户更快速的获取到自己问题的…

02 C语言计算

02 C语言计算 0、编程练习题 #include<stdio.h>int main(int argc,char const *argv[]){int a,b;scanf("%d %d",&a,&b);printf("%d %d %d\n",a,b,a b);printf("%d - %d %d\n",a,b,a - b);printf("%d * %d %d\n",a…

C/C++每日一练(20230305)

目录 1. 整数分解 ☆ 2. 二叉树的最小深度 ★★ 3. 找x ★★ 1. 整数分解 输入一个正整数&#xff0c;将其按7进制位分解为各乘式的累加和。 示例 1&#xff1a; 输入&#xff1a;49 输出&#xff1a;497^2示例 2&#xff1a; 输入&#xff1a;720 输出&#xff1a;720…

Java分布式事务(三)

文章目录&#x1f525;MySQL事务-MySQL中锁的分类&#x1f525;MySQL事务-MySQL中的死锁问题&#x1f525;MySQL事务-MySQL中锁的分类 MySQL中锁的分类 从本质上讲&#xff0c;锁是一种协调多个进程或多个线程对某一资源的访问的机制&#xff0c;MySQL使用锁和MVCC机制实现了…

TIA博途中将硬件目录更改为中文的具体方法演示

TIA博途中将硬件目录更改为中文的具体方法演示 基本步骤可参考如下: 第一步: 第二步: 具体的操作演示: 如下图所示,在所示的目录中找到zh-chs文件夹,删除或修改文件夹的名称均可,这里建议大家修改文件夹的名称,防止以后需要恢复成英文目录, 如下

3DEXPERIENCE Works 成为了中科赛凌实现科技克隆环境的催化剂

您的企业是否想过实现设计数据的统筹管理&#xff0c;在设计上实现标准化&#xff0c;并把每位设计工程师串联起来协同办公?中科赛凌通过使用3DEXPERIENCE Works 实现了上述内容&#xff0c;一起来看本期案例分享吧!中科赛凌 通过其自主研发的单压缩机制冷技术实现零下190℃制…

Hbase 的复制

HBase默认采用异步复制的方式同步数据&#xff0c;即客户端执行完put之后&#xff0c;RegionServer的后台线程不断地推送HLog的Entry到Peer集群。这种方式一般能满足大多数场景的需求&#xff0c;例如跨集群数据备份、HBase集群间数据迁移等。但是HBase 1.x版本的复制功能&…

【群晖Drive私有云】利用cpolar内网穿透实现公网远程群晖Drive

文章目录前言1.群晖Synology Drive套件的安装1.1安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用3. 结语前言 群晖作为专业的数据存储中心&#xff0…

路径规划 | 图解动态A*(D*)算法(附ROS C++/Python/Matlab仿真)

目录0 专栏介绍1 什么是D*算法&#xff1f;2 D*算法核心概念一览3 D*算法流程图4 步步图解&#xff1a;算法实例5 算法仿真与实现5.1 ROS C实现5.2 Python实现0 专栏介绍 &#x1f525;附C/Python/Matlab全套代码&#x1f525;课程设计、毕业设计、创新竞赛必备&#xff01;详…

【工具】logseq 使用分享

Github: https://github.com/logseq/logseq 三月八日国际劳动妇女节&#xff0c;当然要分享一款好用的记事本软件。 这次介绍的笔记本软件叫 logseq。 logseq 与传统的笔记软件不同&#xff0c;传统的笔记软件有各种数据单元&#xff08;post、title、refs、category、tags、…

智慧灌区信息化解决方案

系统概述智慧灌区信息化解决方案主要对对灌区的水情、雨情、土壤墒情、气象等信息进行监测&#xff0c;对重点区域进行视频监控&#xff0c;同时对泵站、闸门进行远程控制&#xff0c;实现了信息的测量、统计、分析、控制、调度等功能。为灌区管理部门科学决策提供了依据&#…

VITA/PYTHON/LUPA families

Image Sensor Group Top to Bottom Portfolio in Industrial Imaging Machine Vision • Factory automation and inspection • Robotic vision • Biometrics High-End Surveillance • Aerial Surveillance • Intelligent Traffic Systems (ITS) • Mapping Medical and Sc…

【UML】软件需求说明书

目录&#x1f981; 故事的开端一. &#x1f981; 引言1.1编写目的1.2背景1.3定义1.4参考资料二. &#x1f981; 任务概述2.1目标2.2用户的特点2.3假定和约束三. &#x1f981; 需求规定3.1 功能性需求3.1.1系统用例图3.1.2用户登录用例3.1.3学员注册用例3.1.4 学员修改个人信息…

Uipath DataTable-FilterDataTable(筛选数据表)

FilterDataTable(筛选数据表) 活动描述 FilterDataTable(筛选数据表)&#xff1a;通过在“筛选器向导”窗口中指定条件来筛选“DataTable”数据表变量&#xff0c;可以根据在该向导中指定的逻辑条件保留或删除行或列。使用如下图&#xff1a; FilterDataTable(筛选数据表)属…

Graph Partition: Edge cut and Vertex cut

Graph PartitionEdge cut and Vertex cutEdge cutVertex cut实际如何进行点分割和边分割的呢&#xff1f;Graph store format情况1&#xff1a;按照边列表存储&#xff1a;情况2&#xff1a;按照邻接表存储&#xff1a;Edge cut and Vertex cut 图结构描述了数据流动&#xff…

项目经理必看!常用的项目管理工具及方法

本文为你介绍&#xff1a;1、好用的项目管理工具&#xff1b;2、项目管理方法 随着企业日益复杂的业务流程和庞大的项目数量&#xff0c;如何高效地管理项目成为了必须面对的挑战&#xff0c;许多企业开始使用项目管理工具和方法来更好地管理项目。 今天我就来介绍几个好用的…

【UE】大世界子关卡StreamingLevel加载流程

受限于硬件&#xff0c;当项目需要制作大世界的时候&#xff0c;整张大地图无法也没必要全部加载进内存。和所有支持大世界的引擎一样&#xff0c;UE采取了分块加载的方式&#xff1a;除了一个持久关卡&#xff08;PersistentLevel&#xff09;的加载以外&#xff0c;采用的都是…

网络通信快速入门

&#x1f3e1;个人主页 &#xff1a; 守夜人st &#x1f680;系列专栏&#xff1a;Java …持续更新中敬请关注… &#x1f649;博主简介&#xff1a;软件工程专业&#xff0c;在校学生&#xff0c;写博客是为了总结回顾一些所学知识点 目录网络编程实现网络编程的三要素&#x…

少儿编程 电子学会图形化编程等级考试Scratch一级真题解析(选择题)2022年12月

少儿编程 电子学会图形化编程等级考试Scratch一级真题解析2022年12月 选择题(共25题,每题2分,共50分) 1、小明想在开始表演之前向大家问好并做自我介绍,应运行下列哪个程序 A、 B、 C、 D、 答案:D

【MySQL】第17章_触发器

第17章_触发器 在实际开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;有 2 个或者多个相互关联的表&#xff0c;如商品信息和库存信息分别存放在 2 个不同的数据表中&#xff0c;我们在添加一条新商品记录的时候&#xff0c;为了保证数据的完整性&#xff0c;必须同时…