Verilog连续赋值、过程赋值、过程连续赋值总结

news/2024/11/15 13:38:07/文章来源:https://www.cnblogs.com/nullbeer/p/18196655

最近总是遇到systemverilog的赋值问题,查看了一下手册发现SV的赋值方式总的还是继承了verilog的赋值方式,而且verilog赋值方面的资料比较多,所以就写了先写一篇关于verilog的赋值总结。

连续赋值

连续赋值就是一旦赋值,输出将随输入改变而变化,一旦修改输入则立刻体现在输出上。

input a,b;
wire out;assign out = a & b;
  • 要求赋值左侧必须为net型
  • 关键词assign
  • 赋值过程类似于物理场景的导线连接,out将跟随a异或b的电路变化而变化,一旦a或b有修改则立刻体现在输出上。
  • 连续赋值属于并发执行,与书写顺序无关,不论在何时赋值都会在一开始执行。
  • 对于同一参数多次赋值,视情况和强度而定,但不要进行该操作。
  • 常用于组合逻辑

过程赋值

过程赋值只会在赋值时刻进行改变,其余时间保持不变,而且根据使用的赋值符号=<=,分为阻塞赋值和非阻塞赋值。

阻塞赋值 =

//例1
initial beginai            = 4'd1 ; 	  	//(1)ai            = 4'd2 ;   		//(2)//ai 最此刻为 2#20 ;                    		//(3)ai            = 4'd3 ;     	//(4)bi            = 4'd4 ;     	//(5)value_blk     = ai + bi ;  	//(6)//value_blk 最终为7#40;end
  • 要求赋值左侧必须为reg、integer, real, time-variable, or memory(等非net型);
  • 赋值过程在initial或always块中;
  • 阻塞赋值类似于C语言的赋值方式,后面的赋值会覆盖掉前面的赋值,执行顺序为1->2->...->6,类似软件编程的赋值方式,随时赋值随时开始。
  • 最终结果与书写顺序相关.
  • 常用于testbench测试用例/组合逻辑.

非阻塞赋值 =>

//例1
initial beginai            = 4'd1 ; 	  	//(1)bi            = 4'd2 ;   		//(2)//ai和bi的初始值分别为1,2#20 ;                    		//(3)//非阻塞赋值ai            <= 4'd3 ;     	//(4)bi            <= 4'd4 ;     	//(5)value_nonblk     <= ai + bi ;  	//(6)#40;//value_blk 最终为3
end

image

  • 要求赋值左侧必须为reg型
  • 关键词为initial或always
  • 非阻塞赋值类似于物理电路中的时序电路,其中可以这样理解代码执行顺序,1->2->3之后其他的非阻塞赋值(4)(5)(6)不着急执行,而是列入到"事件队列"中,一直存到#40需要被执行前,即下一个时刻需要执行前,(4)(5)(6)将会被同时执行,此时对于value_nonblk而言其对应的ai和bi依旧是1和2,因此结果为3,可用于时序电路建模。
  • 最终结果与书写顺序相关
  • 常用于时序逻辑

下面使用阻塞赋值和非阻塞分别描述构成的组合逻辑和时序逻辑。

module top_module(
input a, output reg out1, output reg out2);
always@(*)	beginout1 = a;       //a的值直接到out1out2 = out1;    //当前out1直接赋给out2
end                 //也就是说a的值直接out2
endmodule

阻塞赋值在always(*)块下,直接综合时形成了如下的组合逻辑电路。
image

module top_module(
input a, input clk, output reg out1, output reg out2);
always@(posedge clk)	beginout1 <= a;       //a的值赋给out1out2 <= out1;    //此时左侧的out1并不是a的大小,而是上一周期的值,因此这时是将上一周期的out1的值赋给out2
end                                                  
endmodule           

而非阻塞赋值在always(posedge clk)块下,在综合时形成了如下时序电路。
image

⚠️注意
虽然阻塞赋值always@(*) out_block = a & b;和连续赋值assign out = a & b;赋值方式不同,而且左侧采用的分别为reg类型和net类型,但最终综合出来却是一样的组合逻辑电路,都不会出现寄存器,这说明声明的类型与合成的硬件无关。
同样赋值过程并完全等同于硬件实现过程 always@(posedge clk) a <= 1;always@(posedge clk) a = 1; 两者也并没有区别,a作为输出都有寄存器的出现,也就是赋值方式与硬件实现过程并不相同这主要是Verilog语法的历史遗留问题, 只采用声明的类型和赋值方式很难区分,而systemverilog干脆直接用logic代替了wire和reg两种类型,对声明的类型不做区分【难办,难办就别办了~~】,因此了解综合后的硬件电路,要重点关注语法本身是组合还是时序。

因此作为一个ICer,不能为了炫技而采用各种花里胡哨的技巧,而是采用一些原则来保证自己的代码可以更便于观察。因此为了更清晰的描述出确定的电路, 在使用非阻塞与阻塞赋值时最好遵循以下原则:

  1. 时序电路建模时,使用非阻塞赋值 ;
  2. 锁存器电路建模时,使用非阻塞赋值 ;
  3. 使用always块写组合逻辑电路时,采用阻塞赋值;
  4. 在同一个always块中同时建立时序和组合逻辑电路时,用非阻塞赋值 ;
  5. 在同一个always块中不要同时使用非阻塞赋值和阻塞赋值 ;
  6. 不要在多个always块中,为一个变量赋值;
  7. 用$strobe系统任务来显示用非阻塞赋值的变量值;
  8. 在赋值时不要使用#0延迟【有时间了再去聊原因】;

遵循以上逻辑可以消除90%-100%的在仿真中产生的竞争冒险现象,有助于正确编写可综合硬件。
image


过程连续赋值

连续赋值通过assign语句驱动net类型变量,而过程赋值通过initial和always块驱动reg类型的变量。这两种驱动方式基本上可以构成常用的电路,但verilog又给出了一种赋值方式,即过程连续赋值,可以通过覆盖现有的赋值,并在一定时期内驱动net、reg数据变量。
主要有两对过程连续赋值:assign-deassignforce-release。但这种赋值方式最好不要用于设计,会使得代码难以理解,更多用于testbench,在测试时,为出现对应结果,将设计中某些必要的信号强制赋为期待值。

assign-deassign

assign通过在有一段时间内覆盖掉现有的过程赋值控制的reg变量的值。并在执行deassign之后,就会释放掉过程连续赋值,但可以保持一段时间,直到其他赋值进行修改。

module assign_deassign_ex;reg [3:0] d1;initial begin$monitor("At time T = %0t: d1 = %0d", $time, d1);d1 = 5;#20 d1 = 7;endinitial begin#5;assign d1 = 3;	//直接覆盖掉d1从5->3#5 deassign d1; //d1释放之后,d1维持一段时间,在#20时刻从5变为7$display("At time T = %0t: deassign d1", $time);end
endmodule//输出结果
At time T = 0: d1 = 5
At time T = 5: d1 = 3
At time T = 10: deassign d1
At time T = 20: d1 = 7

force-release

force 和 release 语句通过在一段时间内覆盖现有的过程、连续或过程连续赋值来控制 net 和 reg 数据类型变量值,force的权限比assign要高,可以覆盖掉其他的赋值方式。release同deassign一样,可以释放掉赋值,但对于过程赋值和过程连续赋值前可以保持前一个值。而对于net类型,直接恢复到前一个连续赋值的值。

module assign_deassign_ex;reg [3:0] d1;wire [3:0] d2;assign d2 = 2;initial begin$monitor("At time T = %0t: d1 = %0d, d2 = %0d", $time, d1, d2);d1 = 5;#20 d1 = 7;endinitial begin#5;$display("At time T = %0t: force d1 and d2", $time);force d1 = 3;		//强制将d1赋值为3force d2 = 4;		//d2赋值为4#5 release d1;	//d1可以保持为3,一直到#20恢复为7release d2;			//d2一旦释放则恢复到2$display("At time T = %0t: release d1 and d2", $time);end
endmodule//输出
At time T = 0: d1 = 5, d2 = 2
At time T = 5: force d1 and d2
At time T = 5: d1 = 3, d2 = 4
At time T = 10: release d1 and d2
At time T = 10: d1 = 3, d2 = 2
At time T = 20: d1 = 7, d2 = 2

参考文献
[1] Procedural continuous assignments - VLSI Verify-教程类
[2] Verilog Assignments-教程类
[3] Verilog 过程连续赋值-教程类
[4] How does SystemVerilog force work? -问答类

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单题 加强版

由简单版中,我们已经推出了 \[\sum_{d=1}^n\mu^2(d)d^{k+1}\sum_{t=1}^{\lfloor {\frac{n}{d}} \rfloor}\mu(t)t^k\sum_{i=1}^{\lfloor {\frac{n}{dt}} \rfloor}\sum_{j=1}^{\lfloor {\frac{n}{dt}} \rfloor}(i+j)^k \]我们设\(T=td\),则 设\(S(x)=\sum_{i=1}^x\sum_{j=1}^x(i…

视野修炼-技术周刊第94期 | 2024 开发者调查报告

① Stack Overflow 2024 开发者调查报告 ② React 2023 调查结果 ③ Sendune - HTML 邮件制作工具 ④ hash-wasm - 哈希函数库 ⑤ ky - 基于 fetch 的 HTTP 客户端 ⑥ 英:前端构建概述 ⑦ Album AI - AI 图片检索 ⑧ AI Emoji Search欢迎来到第 94 期的【视野修炼 - 技术周刊】…

IDEA 2024最新永久安装使用教程

在软件开发的世界里,IntelliJ IDEA作为Java、Kotlin等多语言开发者的首选IDE(集成开发环境),以其强大的功能、灵活的扩展性和卓越的智能辅助功能赢得了广泛的赞誉。随着人工智能(AI)技术的飞速发展,IntelliJ IDEA也紧跟时代步伐,通过引入一系列AI编程插件,极大地提升了…

c语言中给int类型变量赋值double型数值; 给double型变量赋值int型数据

001、 给int型变量赋值double型数据[root@PC1 test]# ls test.c [root@PC1 test]# cat test.c ## 测试程序 #include <stdio.h>int main(void) {int i;i = 8.583;printf("i = %d\n", i); // 其返回值是去掉了小数点后…

408 数据结构线性表算法

第一章 线性表 定义 :线性表是具有 相同数据类型 的n(n>=0)个数据元素的 有限序列 。 线性表的表示 :若用L命名,表示:L=(a1,a2,a3,a4,a5,……,an) 线性表的逻辑特性 :a1:唯一的表头元素 an:唯一的表尾元素 除去a1:每个元素有且仅有一个直接前驱 除去an:每个元素有…

c 语言中 用 %d输出double型数据;%f输出int型数据

001、 %f输出int型数据,其值为0.[root@PC1 test]# ls test.c [root@PC1 test]# cat test.c ## 测试程序 #include <stdio.h>int main(void) {int i = 10;printf("i = %f\n", i); // %f输出int型数据return 0; } [root@PC1 test]# gcc te…

一觉醒来,虚拟机的IP变了【解决】

为了模拟服务器,我使用了虚拟机来连接我的后端程序。配置了很多信息,就比如我的虚拟机ip。但是一觉醒,来再次启动发现虚拟机的ip变了!!!于是,我查阅了相关资料......解决办法 1修改配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33TYPE=Ethernet PROXY_METHOD=…

408 数据结构队列算法

第三章 队列 3.1顺序队列 #define MAXSIZE 64 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int front; //队头指针int rear; //队尾指针int size; //队列大小 }SeQueue;//初始化队列 void initQueue(SeQueue& Q) {//对数据元素进行初始化,防止出现脏数…

408数据结构树算法

第四章 树 4.1 二叉树的顺序存储 #define MAXSIZE 16 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int size; }Tree;//初始化二叉树 void initTree(Tree& T) {for (int i = 0; i < MAXSIZE; i++) {T.data[i] = 0; //假设0表示空节点}T.size = 0; } /…

多项式全家桶

多项式合集 前置知识:多项式的定义,表示方法,FFT,NTT,微积分等。 注意事项多项式的封装很重要,现在一般都是用将指针传入一个函数的方式来进行多项式操作,如:void Inv(ll *f,ll *g,int n),表示对 \(n\) 次多项式 \(f\) 求逆,结果存在 \(g\) 中。 多项式数组多了一定要…