Verilog中阻塞与非阻塞的区别
阻塞赋值和非阻塞赋值的用法
阻塞赋值和非阻塞赋值的用法说到Verilog编程里的阻塞赋值和非阻塞赋值,哎呀,简直就是两个“冤家”。
你可能听说过它们,但是可能还没有搞清楚到底哪个是“王者”,哪个又是“二把手”。
阻塞赋值和非阻塞赋值就像是两种性格截然不同的人,碰到的情况不同,做的事也不一样,它们互不干涉,但又相互影响。
来聊聊阻塞赋值吧。
阻塞赋值就像你在厨房做饭时,先把锅里的菜炒熟,等菜完全做好了,你才能开始准备下一道菜。
明白了吧?它的特点就是按顺序执行,你要做完当前的事,下一件事才会开始,完全不会“跳过”什么。
比如在Verilog代码里,如果你写了一个“=`赋值语句”,那这个赋值语句就会“阻塞”当前进程,必须等这个赋值完成后,才会继续执行后面的操作。
所以,如果你把多个赋值语句写在一起,执行的顺序是很重要的!有时候你会发现,哎,这个赋值好像搞得我有点儿“慢慢吞吞”了。
对,没错,它就是这么不急不躁。
比方说你要计算一个信号值时,必须等上一个信号的值被计算出来,再处理下一个,这种“循序渐进”的方式就是阻塞赋值的代表作。
但是,说到非阻塞赋值,它就完全不一样了。
非阻塞赋值好像一个特别会multitasking(多任务处理)的家伙,啥事都不急,做得也不慢,反正一心二用,顾得了这边又能管得了那边。
在Verilog中,非阻塞赋值就是用“<=”来表示的。
它和阻塞赋值最大的区别就是:它不会等前一个赋值操作完成后才去做下一个,而是直接“放手一搏”,按自己的节奏走。
就好像你炒菜的时候,一边看着锅里的菜,另一边手里已经开始切别的蔬菜了。
这种“同步进行”的方式让你的代码看起来很高效,也能避免在一些需要并行执行的场景下,出现拖沓的情况。
你会发现非阻塞赋值让代码看起来好像更“灵活”一些。
因为它可以在同一时刻进行多个赋值操作,减少了等待的时间,所以特别适合那些需要并行处理的复杂设计。
想象一下,当你在做一台计算机模拟的设计时,非阻塞赋值就好比是一个多线程的系统,不停地切换工作,任何时候都能看似同时处理多个任务,而不会因为一个赋值的完成而停顿下来。
Verilog的delay与阻塞or非阻塞的仿真
仿真、延迟时序ftang 代码技巧用【$display()】+【$time】引入【$display()】对仿真的影响------display若打印变量、变量也作为敏感信号无$display()时候【$monitor()类似】有$display()时候+$display()不使用变量===不会影响逻辑例子---------------2个变量例子1=阻塞+delay在右侧==【process在sleep期间忽略其他事件+唤醒后使用old 右值】在T=100时,触发always块的process执行#50会导致该process进入sleep且因为是=阻塞赋值,则在该process过程中、其不会被新的事件唤醒【也阻塞其他事件】当该process被唤醒的时候,使用旧的右值、并赋给左值【右侧delay使用旧值】例子1续=阻塞+delay在右侧==【更清晰看出sleep期间忽略新的事件】在T=100时候,#50导致process进入sleep------sleep期间忽略、其他事件例子2=阻塞+delay在左侧==【process在sleep期间忽略其他事件+唤醒后使用new 右值】在T=100时,触发always块的process执行#50导致该线程进入睡眠-----在睡眠会忽略其他的事件【添加$display()可知】例子3=非阻塞+delay在右侧==【process立即返回+对new事件敏感+使用old值】在T=100时,触发always块的process执行#50会导致该process进入sleep且因为是<=非阻塞赋值,则不会阻塞后面的语句执行,因后面没有语句该process相当于立即返回【若加打印可以看的比较清楚,立即返回了】在T=120时,因该process返回了,则可以被再次触发对比=若后面还有其他阻塞语句话【如#30】,则该process不会立即返回------在sleep期间、会忽略新的事件例子4=非阻塞+delay在左侧==在T=100时,触发always块的process执行因为#50在左侧=则该process进入sleep------在sleep期间、忽略新的触发事件【则上面提取的概念】--------sleep方面+process返回【整个process做sleep】+【部分子式做sleep,但process返回】【process做sleep】与【process返回】-----process返回后,才会响应new触发事件----------实例阻塞赋值----会阻塞整个process,导致process不能返回【left = right】【left=#delay right】会先计算right,在做赋值#delay在左侧-----会阻塞整个process-------------------3个变量例子a1=阻塞+delay在右侧+级联在T=100时,触发always块的process执行#50导致process做sleep在process Wakeup后,做更新leftValue的操作【先计算右值+产生update事件+process做sleep+唤醒后更新左值+再做本process后面操作】在T=150时,process唤醒因第1条语句是阻塞、则process没有返回的----执行完b更新后、b的值变化但第2句仍为阻塞赋值、则其阻塞process返回-----则前面的b变化、不会导致process重新被触发对比=若第2句为非阻塞、则在T=150时候,先计算第2句右值+process返回+再更新第2句左值【因b在第1句更新的、那时process未返回,则也不会触发新的process】再对比=非阻塞赋值,是先process返回、再执行更新左值例子a2=阻塞+delay在右侧+平行例子a3=阻塞+delay在左侧+级联阻塞=会阻塞process的返回+阻塞process返回前、对敏感信号的响应例子a4=阻塞+delay在左侧+平行例子a5=非阻塞+delay在右侧+级联例子a6=非阻塞+delay在左侧+级联例子a7=非阻塞+delay在右侧+平行【上面提取】【产生update事件】+【更新左值】是不同的概念,也是不同的调度顺序-----【<=为先产生updateEvent+再process返回+最新更新左值】+【=先产生updateEvent+更新左值+最后process返回】即阻塞---不仅阻塞后面的语句执行,也阻塞process返回+也阻塞process对newEvent敏感【left=#delay right】==【先计算右值+产生update事件+process做sleep+唤醒后更新左值+再做本process后面操作(若无操作则process返回)】【left<=#delay right】==【先计算右值+产生update事件+process做sleep+唤醒后process 返回(若后续无阻塞语句)+更新左值】【process在返回前、会忽略new触发事件】【#delay】从某种程度上、相当于阻塞在左侧则#delay相当于单独的1句,【#delay left=right;】等价于【#delay; left=right; 】在右侧则这里有1个语法相关执行顺序,先执行右侧表达式,再执行delay【left=#delay right;】等价于【tmp= right ; #delay; left= tmp;】小结组合逻辑中,#delay即不能加在左侧,也不能放在右侧----因为在process做sleep时候、会忽略敏感事件时序逻辑中,#delay只能加在右侧---------------编译错误例子3=wire不能用于always中例子4=assign不能带#delay例子2=带反馈的+【always@(*)】与【always】区别波形1=【仅always】产生clk信号波形2=【always@(*)】产生的仅1个脉冲always@(event)=不断的等事件----因@(event)可以单独的使用,则always@(event)=不断的等事件----即@(event)可以理解为=1条语句----则一般而言仿真的process会在等event时候,处于睡眠上面的说明可能存在一些错误,这里想说明的是:当理解上出现问题的时候,用实际的例子来帮助理解;并不断的修正;注重:仿真vs可综合语句是有差异的,仿真便于debug(特别是大型的工程),多做coding自然会讲这2者分辨出来;。
Verilog中的一些语法和技巧
Verilog中的⼀些语法和技巧1、.2、.3、Reg型的数据类型默认初始值为X。
reg型数据可以赋正值也可以赋负值,但是当⼀个reg型数据是⼀个表达式的操作数的时候,他的值被当做⽆符号数及正值。
4、在数据类型中?和Z均表⽰⾼阻态。
5、Reg型只表⽰被定义的信号将⽤在“always”模块内,并不是说reg型⼀定是寄存器或触发器的输出。
虽然reg型信号常常是寄存器或触发器的输出但是并不⼀定总是这样。
6、Verilog语⾔中没有多维数组的存在。
Memory型数据类型是通过扩展reg型数据的弟⼦和范围来⽣成的。
其格式如下reg[n-1:0]存储器名[m-1:0];7、在除法和取余的运算中结果的符号和第⼀个操作数的符号位是相同的。
8、不同长度的数据进⾏运算:两个长度不同的数据进⾏位运算时,系统会⾃动地将两者按有端对齐,位数少的操作数会在相应的⾼位⽤0填满以便连个操作数安慰进⾏操作。
9、= = =与!= = =和= =与!= =的区别:后者称为逻辑等是运算符,其结果是2个操作数的值决定的。
由于操作书中某些位可能不定值x和⾼阻态z结果可能是不定值x。
⽽ = = =和!= = =运算符对操作数的⽐较时对某些位的⾼阻态z和不定值x也进⾏⽐较,两个操作数必须完全⼀致,其结果才是1,否则是0.10、⾮阻塞和阻塞赋值⽅式:⾮阻塞赋值⽅式(如a<=b)上⾯语句所赋得变量值不能⽴即被下⾯语句所⽤,(2)快结束后才能完成这次赋值操作 3在编写克综合的时序逻辑模块时这是最常⽤的赋值⽅法。
阻塞赋值(如a=b)赋值语句执⾏完后,块才结束 2 b的值在赋值语句完成后⽴即执⾏ 3在时序逻辑使⽤中,可能产⽣意想不到的结果。
11、模块的描述⽅式:(RTL为寄存器传输级描述)“(1)数据流描述⽅式:数据流⾏描述主要⽤来描述组合功能,具体⽤“assign”连续赋值语句来实现。
分为两种a、显式连续赋值语句;连线型变量类型[连线型变量为快]连线型变量名Assign #(延时量)连线型变量名=赋值表达式;显式连续赋值语句包含了两条语句;第⼀条是对连线型变量的进⾏类型说明的说明语句;第⼆句是对这个已得到声明的连线型变量进⾏连续赋值语句。
阻塞赋值和非阻塞赋值深度解析——仿真事件的调度
阻塞赋值和⾮阻塞赋值深度解析——仿真事件的调度来源:EETOP BLOG 作者:acgoal/blog/html/46/553746-51456.html下⾯有⼀段verilog代码和仿真⽂件,⽤的是VCS仿真和编译⼯具。
我们来研究⼀下不同的驱动赋值⽅式对仿真结果的影响。
下⾯我把我做的例⼦和⼤家分享⼀下。
设计源代码如下:`timescale 1ns/1psmodule counter (data_out0, data_out1,clk,rst_n, data_in0, data_in1);output[3:0] data_out0;output [3:0] data_out1;input [3:0] data_in0;input [3:0] data_in1;input clk;inputrst_n;reg [3:0] data_out0;reg [3:0] data_out1;always @(posedge clk or negedge rst_n)begin if(!rst_n) begin data_out0 <= 4'd0; end else begin data_out0 <=data_in0; endendalways @(posedge clk) begin if(!rst_n) begin data_out1 <= 4'd0; end elsebegin data_out1 <= data_in1; endendendmodule这⾥有两段,分别表⽰带异步复位和带同步复位的。
测试程序如下:`timescale 1ns/1psmodule tb_top;reg clk;reg rst_n;wire [3:0] data_out0;wire [3:0] data_out1;reg[3:0] data_in0;reg [3:0] data_in1;counter u_counter0(.clk(clk), .rst_n(rst_n),.data_out0(data_out0), .data_out1(data_out1), .data_in0(data_in0),.data_in1(data_in1));initial begin clk=1'b0;endalways begin #3 clk=~clk;endinitial begin rst_n= 1'b1; #15 rst_n = 1'b0; #180 rst_n = 1'b1; #200 $finish;endinitial begin data_in0 <= 0;wait(!rst_n); wait(rst_n); @(posedge clk); data_in0 <= 1; @(posedge clk); data_in0 <= 2;@(posedge clk); data_in0 <= 4; @(posedge clk); data_in0 <= 8; @(posedge clk); data_in0<= 0;endinitial begin data_in1 <= 0; wait(!rst_n); wait(rst_n); @(posedge clk); data_in1 <= 1;@(posedge clk); data_in1 <= 2; @(posedge clk); data_in1 <= 4; @(posedge clk); data_in1<= 8; @(posedge clk); data_in1 <= 0;endinitial begin $vcdpluson();endendmodule注意这⾥红⾊的部分,data0和data1的驱动都⽤⾮阻塞赋值。
VHDL非阻塞赋值理解(转载)
在always语句块中,verilog语言支持两种类型的赋值:阻塞赋值和非阻塞赋值。
阻塞赋值使用“=”语句;非阻塞赋值使用“<=”语句。
注意,千万不要将这两种赋值方法与assign赋值语句混淆起来,assign赋值语句根本不允许出现在always语句块中。
位于begin/end块内的多条阻塞赋值语句是串行执行的,这一点同标准的程序设计语言是相同的。
但是多条非阻塞赋值语句却是并行执行的,这些非阻塞赋值语句都会在其中任何一条语句执行完成之前开始执行。
这正是硬件电路的特点,因为实际的逻辑门电路都是独立运转的,而不是等到其他门电路运转结束之后自己才开始运转。
下面我们以描述移位寄存器的两种方法为例来讲述两种赋值类型的区别。
在下面的这种描述中,第一个触发器中的数据被移到第二个触发器中,第二个触发器中的数据被移到第三个触发器中……如此继续下去,直到最后一个触发器中的数据被移出该寄存器为止。
1module shiftreg (input clk,2input sin,3outout reg [3:0]q);//这是正确使用非阻塞赋值的实例4a lways @(posedge clk)5b egin6q[0] <= sin;//非阻塞赋值:<=7q[1] <= q[0];8q[2] <= q[1]9q[3] <= q[2];10//这里写作q <= {q[2:0],sin};更简单更好一些11e nd12endmodule非阻塞赋值语句的功能是使得所有语句右侧变量的值都同时被赋给左侧的变量。
因此,在上面的实例中,q[1]得到的是q[0]的原始值,而非sin的值(在第一条语句中,sin的值被赋给了q[0])。
这正是我们期望得到的实际硬件电路。
当然,我们可以把上边的四条语句合并写成一条简短的语句:q<={q[2:0],sin}。
阻塞赋值语句的功能更接近于传统的程序设计语言,但是阻塞赋值语句并不是准确的硬件工作模型。
Verilog学习笔记--运算符与阻塞非阻塞语句
verilog中的位运算符,缩位运算符和逻辑运算符的说明
1,位运算符
按位运算的运算符是位运算符,原来的操作数有几位,结果就有几位,若两个操作数位数不同,则位数短的操作数左端会自动补0(两个数右端对齐,位数少的操作数会在相应的高位补0)。
(1),按位取反:~
(2),按位与:&
(3),按位或:|
(4),按位异或:^
(5),按位同或:^~或~^
2,缩位运算符(又称归约运算符)
缩位运算符是单目运算符,按位进行逻辑运算,结果是一位值!
(1),与缩位运算符:&
(2),或缩位运算符:|
(3),异或缩位运算符:^
(4),与,或,异或运算符和非运算符组成的复合运算符:~&,~|,~^
3,逻辑运算符(逻辑关系运算)
(1),逻辑与:&&
(2),逻辑或:||
(3),逻辑非:!
其中,逻辑与和逻辑或是双目运算符,逻辑非是单目运算符。
如果操作数是多位的,则将操作数看做整体,若操作数中每一位都是0值则为逻辑0值,若操作数当中有1,则做位逻辑1值。
4,相等与全等运算符
(1),==
(2),!=
(3),===
(4),!==
== 、!= 、===、!== 符号之间不能有空格。
“==”和“!=”称作逻辑等式运算符,其结果由两个操作数的值决定。
由于操作数可能是x或z,其结果可能为x;
“===”和“!==”常用于case表达式的判别,又称作cae等式运算符。
其结果只为0和1.如果操作数中存在x和z,那么操作数必须完全相同结果才为1,否则为0.
逻辑等式运算符和case等式运算符的区别:。
阻塞和非阻塞赋值语句的用法
例1和例2的RTL Viewer
例1和例2的仿真波形
非阻塞赋值语句也是出现在initial和always块语 句中,赋值符号是“<=”,格式为 赋值变量 <= 表达式;
在非阻塞赋值语句中,赋值号“<=”左边的赋值 变量也必须是reg型变量,其值不象在过程赋值 语句那样,语句结束时即刻得到,而在该块语句 结束才可得到。
建议:在时序逻辑电路的设计中,采用非阻塞型 赋值语句
always @(posedge clock)
m = 3;
n = 75;
ቤተ መጻሕፍቲ ባይዱ
n <= m;
r = n; 语句执行结束后,r的值是75,而不是3,因为第3行 是非阻塞赋值语句“n <= m”,该语句要等到本块语 句结束时,n的值才能改变。
2016/12/5 5
非阻塞式赋值语句与阻塞式赋值语句
例 1: module DDF3(clk,D,Q); input clk,D; output reg Q; reg a,b; always@(posedge clk) begin a<=D; b<=a; Q<=b; end endmodule 例 2: module DFF3(clk,D,Q); input clk,D; output reg Q; reg a,b; always@(posedge clk) begin a=D; b=a; Q=b; end endmodule
非阻塞式赋值
在过程语句中,比较接近真实的电路赋值 和输出。 有一个特殊的延时操作,而且在赋值过程 中不影响其它同类语句的赋值操作。 同阻塞式赋值一样,允许对同一目标信号 多次赋值或驱动。Verilog规定,被赋值 的目标变量接受最接近过程结束的那一个 驱动源的数据。
eda试题及答案verilog
eda试题及答案verilog1. 请解释Verilog中的阻塞赋值和非阻塞赋值的区别。
答案:在Verilog中,阻塞赋值使用`=`操作符,表示在赋值时会立即执行,并且赋值操作会阻塞后续语句的执行,直到当前赋值完成。
而非阻塞赋值使用`<=`操作符,表示赋值操作会在当前时间单位的末尾执行,不会阻塞后续语句的执行,允许并行执行。
2. 描述Verilog中模块的实例化过程。
答案:在Verilog中,模块的实例化是通过使用模块名后跟实例名和连接端口的列表来完成的。
实例化过程包括指定模块名、实例名以及将端口连接到适当的信号或参数上。
例如:```verilogmodule my_module(a, b, c);output a, c;input b;// ...endmodule// 实例化my_module instance_name(.out1(a), .out2(c), .in(b));```3. 列出Verilog中的基本数据类型。
答案:Verilog中的基本数据类型包括:- 线网类型(wire)- 寄存器类型(reg)- 实数类型(real)- 整型(integer)- 时间类型(time)- 字符串类型(string)4. 说明Verilog中如何使用条件语句。
答案:在Verilog中,可以使用`if`、`case`和`if-else`等条件语句来实现条件控制。
例如,使用`if`语句:```verilogif (condition) begin// 条件为真时执行的代码end else begin// 条件为假时执行的代码end```5. 解释Verilog中的always块的作用。
答案:Verilog中的always块用于描述硬件的时序逻辑和组合逻辑。
always块可以是时序的(使用时钟信号触发),也可以是非时序的(不依赖于时钟信号)。
时序always块通常用于描述寄存器行为,而非时序always块用于描述组合逻辑。
Verilog复习题
Verilog 复习题一、填空题1.用EDA技术进行电子系统设计的目标是最终完成ASIC的设计与实现。
2.可编程器件分为CPLD和FPGA。
3.随着EDA技术的不断完善与成熟,自顶向下的设计方法更多的被应用于Verilog HDL设计当中。
4.目前国际上较大的PLD器件制造公司有ALtera和Xilinx公司。
5.完整的条件语句将产生组合电路,不完整的条件语句将产生时序电路。
6.阻塞性赋值符号为,非阻塞性赋值符号为<=_。
7.有限状态机分为Moore和Mealy两种类型。
8.EDA缩写的含义为电子设计自动化(Electronic Design Automation) |9.状态机常用状态编码有二进制、格雷码和独热码。
10.Verilog HDL中任务可以调用其他任务和函数。
11.系统函数和任务函数的首字符标志为_$_,预编译指令首字符标志为。
12 .可编程逻辑器件的优化过程主要是对速度和资源的处理过程。
13、大型数字逻辑电路设计采用的IP核有软IP、固IP和硬IP。
二、选择题1、已知“a =1b' 1; b=3b'001; ”那么{a,b} =( C )(A) 4b'0011 (B) 3b'001 (C) 4b'1001 (D) 3b'1012、在verilog中,下列语句哪个不是分支语句? ( D )(A) if-else (B) case (C) casez (D) repeat3、V erilog HDL语言进行电路设计方法有哪几种( 8分)①自上而下的设计方法(Top-Down )②自下而上的设计方法(Bottom-Up )③综合设计的方法4、在verilog 语言中,a=4b'1011,那么&a= (D )(A) 4b'1011 (B) 4b'1111 (C) 1b'1 (D) 1b'05、在verilog语言中整型数据与(C )位寄存器数据在实际意义上是相同的。
verilog 运算操作符号
Verilog运算操作符号一、概述在数字电路设计和硬件描述语言(HDL)中,运算操作符号是非常重要的,它们用来表示数字电路中的逻辑操作和数据处理。
Verilog是一种常用的硬件描述语言,其中也包含了丰富的运算操作符号,本文将对Verilog中常见的运算操作符号进行详细介绍。
二、赋值操作符号1. 阻塞赋值(=)阻塞赋值用“=”表示,在Verilog中用于将右侧表达式的值赋给左侧的变量。
阻塞赋值会在当前时间点立即执行,并且会导致模拟的并行行为。
2. 非阻塞赋值(<=)非阻塞赋值用“<=”表示,在Verilog中用于将右侧表达式的值赋给左侧的变量。
非阻塞赋值会延迟一个时间段后才执行,而且多个非阻塞赋值会按顺序执行,模拟的是时序逻辑。
三、逻辑运算操作符号1. 与操作()与操作符号“”用于执行逻辑与操作,对两个操作数的每一位执行与操作,只有两个操作数对应位都为1时结果才为1。
2. 或操作(|)或操作符号“|”用于执行逻辑或操作,对两个操作数的每一位执行或操作,只要两个操作数对应位中有一位为1,结果就为1。
3. 异或操作(^)异或操作符号“^”用于执行逻辑异或操作,对两个操作数的每一位执行异或操作,当两个操作数对应位相同时结果为0,不同时结果为1。
四、算术运算操作符号1. 加法操作(+)加法操作符号“+”用于执行加法操作,对两个操作数进行加法运算。
2. 减法操作(-)减法操作符号“-”用于执行减法操作,对两个操作数进行减法运算。
3. 乘法操作(*)乘法操作符号“*”用于执行乘法操作,对两个操作数进行乘法运算。
4. 除法操作(/)除法操作符号“/”用于执行除法操作,对两个操作数进行除法运算。
五、移位运算操作符号1. 左移操作(<<)左移操作符号“<<”用于将操作数向左移动指定的位数。
2. 右移操作(>>)右移操作符号“>>”用于将操作数向右移动指定的位数。
verilog非阻塞赋值语句
verilog非阻塞赋值语句
嘿,你知道 Verilog 里的非阻塞赋值语句不?这玩意儿可神奇啦!
就好像是在数字电路的世界里搭起了一座特别的桥梁。
比如说,在一个复杂的电路设计中,你想象一下,信号们就像是一
群忙碌的小蜜蜂,飞来飞去传递着信息。
非阻塞赋值语句呢,就像是
给这些小蜜蜂规划了特定的路线,让它们能准确无误地到达目的地。
“哎呀,这非阻塞赋值语句到底有啥用啊?”你可能会这么问。
嘿,
它的用处可大了去了!它能让你的代码运行得更加高效、更加准确。
就好比你走路,走直线总比绕弯子快吧!
有一次,我在设计一个大型电路的时候,一开始没搞清楚非阻塞赋
值语句,结果那电路运行得乱七八糟,就像没头苍蝇似的。
后来,我
认真研究了一下,把非阻塞赋值语句用对了,哇塞,那效果,简直就
像给电路打了一针兴奋剂!
你看,这非阻塞赋值语句不就是我们数字电路设计师的秘密武器嘛!它能让我们的设计变得更加完美,更加出色。
咱再说说它和阻塞赋值语句的区别,那可真是一个天上一个地下啊!阻塞赋值语句就像是个急性子,一下子就把事情做完了,有时候可能
会引发一些意想不到的问题。
而非阻塞赋值语句呢,就像个慢性子,
慢悠悠地,但却能把事情处理得妥妥当当。
总之,Verilog 里的非阻塞赋值语句绝对是个值得我们好好研究和利用的好东西。
你可别小瞧它哦,不然在电路设计的道路上你可要吃亏啦!我的观点就是,非阻塞赋值语句是 Verilog 中非常重要且神奇的一部分,掌握了它,你就像是掌握了打开数字电路宝藏大门的钥匙!。
Verilog中的延时、阻塞与非阻塞赋值仿真
从仿真语义的角度看Verilog中的延时、阻塞与非阻塞赋值1 Verilog中的延时Verilog没有和VHDL中类似的最小延时概念,所有的延时都由符号“#”来定义,如果没有这个符号就意味着没有延时,清单1中描述了一个有关延时的简单例子。
清单1 简单的延时wire #5 Y = A & B;清单1 中使用持续赋值语句描述了一个两输入端与门逻辑,并且在表达式前插入了5ns (#5)的延时,意义为Verilog仿真器会在5ns的延时后将A和B相与赋值给Y。
通过这个例子可以看出,延时的插入只需要在原本的语句中加入“#”关键字即可,但在实际的使用中却经常产生错误,实际中的延时时间是由具体的硬件电路来决定的。
使我们更深入的理解Verilog中的延时,更加关注描述的电路意义而不是描述语句本身,Verilog也是一种机于硬件的语言。
1.1 实际中的延时在实际的电路中,只存在着两种延时行为,一个是惯性延时,另一个是传输延时。
1.1.1 惯性延时(Inertial Day)惯性延时通常在信号通过逻辑门的时候发生,图1所示是信号通过一个具有5ns延迟的非门时的行为。
图1 惯性延时输入信号WireIn有两个高电平脉冲,一个宽度为3ns,另一个宽度为9ns。
当第一个3ns 的脉冲到达非门时,因为其宽度小于非门的本身延时(5ns),输出还来不及建立低电平,输入脉冲就已经过去,所以在输出信号WireOut上没有体现出第一个3ns脉冲的响应。
第二个脉冲宽度为9ns,大于非门的本身延时,所以在脉冲上升沿5ns之后,WireOut输出了一个宽度为9ns的低脉冲,这个脉冲与输入脉冲等宽、反向而且延迟了5ns。
这种延时称为惯性延时或惰性延时。
如果输入的变化过快,小于逻辑门本身的延时,就不会被体现在输出上。
1.1.2 传输延时(Transport Delay)传输延时相对于惯性延时更容易理解,相当于信号通过了一条拥有固定延时的传输线。
阻塞赋值与非阻塞赋值
阻塞赋值与非阻塞赋值作者:zhsj 日期:2015-7-27在Verilog语法中,阻塞赋值和非阻塞赋值是非常难理解的一个概念,尤其是对于初学者,往往搞不懂何时使用非阻塞赋值及何时使用阻塞赋值才能设计出符合要求的电路。
本文是笔者学习此概念时的学习笔记,主要分为概念解析和实例分析,并对一些编程要点进行总结分析,希望对各位初学者有所帮助。
在正式讲解之前先定义两个英文缩写字:RHS——赋值符合右边的表达式或变量;LHS——赋值符号左边的表达式或变量。
一、概念解析1.1 阻塞赋值阻塞赋值操作符为等号(即“=”),当采用阻塞赋值方式赋值时,需要先计算等号右手方向(RHS)部分的值,这时赋值语句不允许任何别的Verilog语句的干扰,直到现行的赋值完成时刻,即把RHS赋值给LHS的时刻,它才允许别的赋值语句的执行。
一般可综合的阻塞赋值操作在RHS不能设定有延迟,即使是零延迟也不允许。
若在RHS上加上延迟,则在延迟期间会阻止赋值语句的执行,延迟后才执行赋值,这种赋值语句是不可综合的,在需要综合的模块中不可使用这种风格的代码。
阻塞赋值的执行可以认为是只有一个步骤的操作,即计算RHS并更新LHS,此时不允许有来自任何其他Verilog语句的干扰。
所谓阻塞的概念是指在同一个always块中,其后面的赋值语句从概念上是前一句赋值语句结束后在开始赋值的,请注意,这只是概念上的先后,而无实质上的延迟。
在使用阻塞赋值时,如果在一个过程块中阻塞赋值的RHS变量正好是另一个过程块中阻塞赋值的LHS变量,这两个过程块又使用同一个时钟沿触发,这时阻塞赋值操作会出现问题,即如果阻塞赋值的顺序安排不好,就会出现竞争。
若这两个赋值操作用同一个时钟沿触发,则执行的顺序是无法确定的,在后面的例子中会看到这一问题。
1.2 非阻塞赋值非阻塞赋值操作符为小于等于号(即“<=”),当采用非阻塞赋值时,在赋值操作开始的时刻计算非阻塞赋值符RHS表达式,赋值操作结束的时刻才更新LHS。
阻塞线程和非阻塞线程
阻塞线程和非阻塞线程阻塞线程和非阻塞线程是计算机领域中常用的概念,它们与多线程编程密切相关。
本文将详细介绍阻塞线程和非阻塞线程的概念、原理和应用。
一、阻塞线程在多线程编程中,阻塞线程是指当一个线程在执行过程中,因为某种原因无法继续执行,从而暂停线程的执行。
阻塞线程的主要原因包括等待用户输入、等待磁盘IO、等待网络IO等。
当线程被阻塞时,它会进入一个等待状态,直到满足某个条件后才能继续执行。
阻塞线程的特点是在等待某个操作完成期间,线程处于休眠状态,不会占用CPU资源。
这样可以提高系统的资源利用率,但同时也会造成程序的执行效率降低。
因此,在设计多线程程序时需要合理地使用阻塞线程,避免过多的阻塞操作,以提高程序的性能。
二、非阻塞线程与阻塞线程相对应的是非阻塞线程。
非阻塞线程是指在执行过程中,线程不会因为某个操作的阻塞而暂停执行,它会继续执行其他任务,而不是等待该操作的完成。
当一个非阻塞线程需要等待某个操作完成时,它会通过轮询或回调的方式来获知操作的结果,从而继续执行后续的任务。
非阻塞线程的特点是能够充分利用CPU资源,提高程序的执行效率。
但同时,非阻塞线程也会增加系统的负担,因为它需要不断地轮询或回调来获取操作结果,这可能会消耗大量的CPU资源。
因此,在使用非阻塞线程时需要权衡系统的负载和性能要求,选择合适的方式来处理。
1. 阻塞线程的应用:(1) 网络编程:在网络编程中,阻塞线程常用于接收和发送数据,等待网络IO的完成。
(2) 用户交互:在图形界面编程中,阻塞线程常用于等待用户输入,如等待按钮点击、等待输入框输入等。
(3) 文件操作:在文件操作中,阻塞线程常用于等待磁盘IO的完成,如读取文件、写入文件等。
2. 非阻塞线程的应用:(1) 服务器编程:在服务器编程中,非阻塞线程常用于处理并发请求,提高服务器的处理能力。
(2) GUI编程:在图形界面编程中,非阻塞线程常用于更新UI 界面,保持界面的流畅和响应性。
verilog赋值原则之阻塞赋值与非阻塞赋值
verilog赋值原则之阻塞赋值与⾮阻塞赋值⼀. 阻塞赋值与⾮阻塞赋值的区别阻塞赋值(Blocking)符号为“ = ”,如:b = a ;可以认为是只有⼀个步骤的操作,计算RHS(等号右边)并更新LHS(等号左边),和C语⾔的赋值很类似。
其中“阻塞”⼆字的意思是在同⼀个块语句中,后⾯的赋值语句是在前⼀个语句赋值结束后才开始赋值的。
⾮阻塞赋值(Non_blocking)符号为“ <= ”,如:b <= a ;当⼀个块语句中有多条赋值语句时,同时计算RHS,计算完成后同时更新LHS。
举⼀个例⼦:`timescale 1ns/1nsmodule blocking ();reg[1:0] a,b,c;initial begina = 2'd1;b = 2'd2;c = 2'd3;#10a = 2'd0;b = a;c = b;$display("a=%d b=%d c=%d ",a,b,c);endendmodule运⾏结果为:若将上述代码的阻塞赋值都改为⾮阻塞赋值,则运⾏结果为:⼆. 赋值原则1. ⾮阻塞赋值只能⽤于寄存器型变量,只能在initial块和always块中使⽤。
阻塞赋值既可以⽤于寄存器型变量,也可以⽤于线⽹型变量。
2. 通常在设计可综合的RTL代码时,描述组合逻辑⽤阻塞赋值,描述时序逻辑⽤⾮阻塞赋值。
3. 在同⼀个块语句中描述组合逻辑和时序逻辑的混合逻辑时,⽤⾮阻塞赋值。
4. 尽量不要在同⼀个always块中同时使⽤阻塞赋值和⾮阻塞赋值。
5. 不能在多个always块中对同⼀个变量进⾏赋值(如果⽤两个always块对同⼀个变量赋值,⽆论其中经过了怎样的条件判断,最终都相当于将两个触发信号连在同⼀个寄存器的CLK端,这显然不合符数字逻辑电路的规则,这样的代码在综合时会报错)。
关于Verilog中的几种赋值语句
关于Verilog中的⼏种赋值语句关键字:assign deassign force release1. 连续赋值语句(Continuous Assignments)连续赋值语句是Verilog数据流建模的基本语句,⽤于对线⽹进⾏赋值,等价于门级描述,是从更⾼的抽象⾓度来对电路进⾏描述。
连续赋值语句必须以关键词assign开始。
连续复制的主要特点是:连续赋值语句的左值可以是⼀下类型之⼀:①标量线⽹②向量线⽹③矩阵中的⼀个元素(该矩阵可以是标量线⽹类型的,也可以是向量线⽹类型的)④向量线⽹的某⼀位⑤向量线⽹的部分位以及上述各种类型的拼接体但是,不能是向量或向量寄存器。
连续赋值语句总是处于激活状态。
只要任意⼀个操作数发⽣变化,表达式就会被⽴即重新计算,并且将结果赋给等号左边的线⽹。
操作数可以是标量或向量的线⽹或寄存器,也可以是函数的调⽤。
赋值延迟⽤于控制对线⽹赋予新值的时间,根据仿真时间单位进⾏说明。
赋值延迟类似于门延迟,对于描述实际电路中的时序是⾮常重要的。
2. 过程赋值语句(Procedural Assignments)过程赋值语句的更新对象是寄存器、整数、实数或时间变量等。
这些类型的变量在被赋值后,其值将保持不变,直到被其他过程赋值语句赋予新值。
过程赋值语句只有在执⾏到的时候才会起作⽤。
过程赋值语句只能在initial或always语句内进⾏赋值,只能对变量数据类型赋值,同时initial和always中只能使⽤过程赋值语句。
过程赋值语句的左值可以是以下类型之⼀:①reg、整形数、实型数、时间寄存器变量或存储器单元②上述各种类型的位选(例如:addr[3])③上述各种类型的域选(例如:addr[31:16])④上⾯三种类型的拼接过程性赋值语句包括两种类型的赋值语句:阻塞赋值(=)和⾮阻塞赋值(<=)(其主要区别详见各类Verilog参考书,这⾥不再详述)。
3. 过程连续赋值语句(Procedural Continuous Assignments)过程连续赋值是在过程块内对变量或线⽹型数据进⾏连续赋值,是⼀种过程性赋值,换⾔之,过程性连续赋值语句是⼀种能够在always或initial语句块中出现的语句。
Verilog中的高级结构
第17章 Verilog中的高级结构
学习内容:
• 任务和函数的定义和调用
• 怎样使用命名块
• 怎样禁止命名块和任务
可以在任务内使用时序控制。 在Verilog中任务定义一个新范围(scope) 要禁止任务,使用关键字disable 。
• • •
从代码中多处调用任务时要小心。因为任务的局部变量的只有一个拷贝,并 行调用任务可能导致错误的结果。在任务中使用时序控制时这种情况时常发生。 在任务或函数中引用调用模块的变量时要小心。如果想使任务或函数能从另 一个模块调用,则所有在任务或函数内部用到的变量都必须列在端口列表中。
任务
下面的任务中含有时序控制和一个输入,并引用了一个module变量, 但没有输出、输入输出和内部变量,也不显示任何结果。 时序控制中使用的信号(例如ck)一定不能作为任务的输入,因为 输入值只向该任务传送一次。
任务
主要特点:
• 任务可以有input,output 和 inout参数。
•
传送到任务的参数和与任务I/O说明顺序相同。尽管传送到任务的参数名 称与任务内部I/O说明的名字可以相同,但在实际中这通常不是一个好的 方法。参数名的唯一性可以使任务具有好的模块性。
组合逻辑举例:全加器
全加器可以由两个组合逻辑UDP实现
// FULL ADDER CARRY-OUT TERM primitive U_ADDR2_C (CO, A, B, CI); output CO; input A, B, Ci; table // A B CI : CO 1 1 ? : 1; 1 ? 1 : 1; ? 1 1 : 1; 0 0 ? : 0; 0 ? 0 : 0; ? 0 0 : 0; endtable endprimitive
阻塞与非阻塞
1、阻塞赋值操作符用等号(即= )表示。
“阻塞”是指在进程语句(initial和always)中,当前的赋值语句阻断了其后的语句,也就是说后面的语句必须等到当前的赋值语句执行完毕才能执行。
而且阻塞赋值可以看成是一步完成的,即:计算等号右边的值并同时赋给左边变量。
例如:当执行“x=next_x;”时,x会立即的到next_x的值。
而下一句“y=x;”必须等到“x=next_x;”执行完毕才能被执行。
由于这两条语句都没有延迟(相当于导线),导致他们的等价语句为“y=next_x;”。
赋值是实时的,计算完右面的马上赋值给左边的,然后再执行下一句,操作时串行的,且在一个alway内完成。
2、非阻塞赋值操作符用小于等于号(即<= )表示。
“非阻塞”是指在进程语句(initial和always)中,当前的赋值语句不会阻断其后的语句。
非阻塞语句可以认为是分为两个步骤进行的:①计算等号右边的表达式的值,(我的理解是:在进入进程后,所有的非阻塞语句的右端表达式同时计算,赋值动作只发生在顺序执行到当前非阻塞语句那一刻)。
②在本条赋值语句结束时,将等号右边的值赋给等号左边的变量。
例如:当执行“x<=next_x;”时,并不会阻断语句“y<=x;”的执行。
因此,语句“y<=x;”中的x的值与语句“x<=next_x;”中的x的值不同:语句“y<=x;”中的x是第一个D触发器的初值(Q0)。
而语句“x<=next_x;”中的x的值是D触发器经过一个同步脉冲后的输出值(Q1)。
基于此这个进程产生了与阻塞赋值进程截然不同的结果,即:产生了移位寄存器的效果,next_x à x à y。
简单理解就是,阻塞赋值是按需执行,非阻塞赋值是并行执行。
为了更好地理解上述要点,我们需要对Verilog 语言中的阻塞赋值和非阻塞赋值的功能和执行时间上的差别有深入的了解。
为了解释问题方便下面定义两个缩写字:RHS –方程式右手方向的表达式或变量可分别缩写为:RHS表达式或RHS变量。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
从上面的例子中,我们可以看出,在阻塞赋值语句中,赋值的次序非常重要,而在非阻塞赋值语句中,赋值的次序并不重要。
下面我们具体分析一下阻塞和非阻塞赋值的语义本质:阻塞:在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句;非阻塞:当前语句的执行不会阻塞下一语句的执行。
先看阻塞赋值的情况:我们来看这段代码:always @(posedge Clk)beginQ1 = D;Q2 = Q1;Q3 = Q2;endalways语句块对Clk的上升沿敏感,当发生Clk 0~1的跳变时,执行该always语句。
在begin...end语句块中所有语句是顺序执行的,而且最关键的是,阻塞赋值是在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句的。
在本例中,D的值赋给Q1以后,再执行Q2 = Q1;同样在Q2的值更新以后,才执行Q 3 = Q2。
这样,最终的计算结果就是Q3 = D。
所有的语句执行完以后,该always语句等待Clk的上升沿,从而再一次触发begin...end 语句。
接下来,再看看非阻塞赋值的情况。
所谓非阻塞赋值,顾名思义,就是指当前语句的执行不会阻塞下一语句的执行。
always @(posedge Clk)beginQ1 <= D;Q2 <= Q1;Q3 <= Q2;end首先执行Q1 <= D,产生一个更新事件,将D的当前值赋给Q1,但是这个赋值过程并没有立刻执行,而是在事件队列中处于等待状态。
然后执行Q2 <= Q1,同样产生一个更新事件,将Q1的当前值(注意上一语句中将D值赋给Q1的过程并没有完成,Q1还是旧值)赋给Q2,这个赋值事件也将在事件队列中处于等待状态。
再执行Q3 <= Q2,产生一个更新事件,将Q2的当前值赋给Q3,这个赋值事件也将在事件队列中等待执行。
这时always语句块执行完成,开始对下一个Clk上升沿敏感。
那么什么时候才执行那3个在事件队列中等待的事件呢?只有当当前仿真时间内的所有活跃事件和非活跃事件都执行完成后,才开始执行这些非阻塞赋值的更新事件。
这样就相当于将D、Q1和Q2的值同时赋给了Q1、Q2和Q3。
注:*仿真器首先按照仿真时间对事件进行排序,然后再在当前仿真时间里按照事件的优先级顺序进行排序。
*活跃事件是优先级最高的事件。
在活跃事件之间,它们的执行顺序是随机的。
阻塞赋值(=)、连续赋值(assign)以及非阻塞赋值的右式计算等都属于活跃事件。
下面通过一个典型案例,进一步说明阻塞赋值和非阻塞赋值的区别。
这里有一个数组:Data[0]、Data[1]、Data[2]和Data[3],它们都是4比特的数据。
我们需要在它们当中找到一个最小的数据,同时将该数据的索引输出到LidMin中,这个算法有点类似于“冒泡排序”的过程,而且需要在一个时钟周期内完成。
例如,如果这4个数据中Data[2]最小,那么LidMin的值则为2。
module Bubble_Up(Rst_n,Clk,Data,Lid_Min);input Rst_n;input Clk;input [5:0] Data [0:3];output [1:0] Lid_Min;reg [1:0] Lid_Min;always @(posedge Clk or negedge Rst_n)beginif (~Rst_n)beginLid_Min <= 2'd0;endelsebeginif (Data[0] <= Data[Lid_Min]) //"<="表示小于等于 beginLid_Min <= 2'd0; //"<="表示非阻塞赋值endif (Data[1] <= Data[Lid_Min])beginLid_Min <= 2'd1;endif (Data[2] <= Data[Lid_Min])beginLid_Min <= 2'd2;endif (Data[3] <= Data[Lid_Min])beginLid_Min <= 2'd3;endendendendmodule我们的原意是首先将Lid_Min设置为一个初始值(任意值都可以),然后将Data[0]~Dat a[3]与Data[Lid_Min]进行比较,每比较一个数,就将较小的索引暂存在Lid_Min中,然后再进行下一次比较。
当4组数据比较完成之后,最小的数据索引就会保留在Lid_Min中。
我们在以上代码中使用了非阻塞赋值,结果发现,仿真波形根本不是我们所需要的功能,如图所示,图中的Data[0]~Data[3]分别为11、3、10和12,Lid_Min的初始值为0。
按道理来说,Lid_Min的计算结果应该为1,因为Data[1]最小,但仿真波形却为2。
为什么会得出这样的结果呢?在时钟上升沿到来以后,且Rst_n信号无效时开始执行以下4个语句,假设这时候的Lid _Min是0,Data[0]~Data[3]分别为11、3、10和12:if (Data[0] <= Data[Lid_Min]) //"<="表示小于等于beginLid_Min <= 2'd0; //"<="表示非阻塞赋值endif (Data[1] <= Data[Lid_Min])beginLid_Min <= 2'd1;endif (Data[2] <= Data[Lid_Min])beginLid_Min <= 2'd2;endif (Data[3] <= Data[Lid_Min])beginLid_Min <= 2'd3;end第一句的if为真,因此执行Lid_Min <= 2’d0,而这时候,Lid_Min并没有立刻被赋值,而是调度到事件队列中等待执行,这是非阻塞赋值的特点。
第二句的if为真,因此执行Lid_Min <= 2’d1,这是Lid_Min也没有立刻被赋值为1,而是调度到事件队列中等待执行。
当前的Lid_Min还是0,没有发生任何变化。
同样,第三句的if也为真,因此执行Lid_Min <= 2’d2,将更新事件调度到事件队列中等待执行。
当前的Lid_Min还是0。
而第四句的if为假,因此直接跳过Lid_Min <= 2’d3,这时跳出always语句,等待下一个时钟上升沿。
在以上的always语句执行完成以后,仿真时间没有前进。
这时存在于事件队列中当前仿真时间上的3个被调度的非阻塞更新事件开始执行,它们分别将Lid_Min更新为0、1和2。
按照Verilog语言的规范,这3个更新事件属于同一仿真时间内的事件,它们之间的执行顺序随机,这就产生了不确定性。
一般的仿真器在实现的时候是根据它们被调度的先后顺序执行的,事件队列就像一个存放事件的FIFO,它是分层事件队列的一部分,如图所示:这3个事件在同一仿真时间被一一执行,而真正起作用的时最后一个更新事件,因此在仿真的时候得到的最终结果时Lid_Min为2。
然后我们想要得到的结果是,在每个if语句判断并执行完成以后,Lid_Min先暂存这个中间值,再进行下一次比较,也就是说在进行下一次比较之前,这个Lid_Min必须被更新,而这一点也正是阻塞赋值的特点,因此我们将代码作如下更改:module Bubble_Up(Rst_n,Clk,Data,Lid_Min);input Rst_n;input Clk;input [5:0] Data [0:3];output [1:0] Lid_Min;reg [1:0] Lid_Min;always @(posedge Clk or negedge Rst_n)beginif (~Rst_n)beginLid_Min <= 2'd0;endelsebeginif (Data[0] <= Data[Lid_Min]) //"<="表示小于等于 beginLid_Min = 2'd0; //"="表示阻塞赋值endif (Data[1] <= Data[Lid_Min])beginLid_Min = 2'd1;endif (Data[2] <= Data[Lid_Min])beginLid_Min = 2'd2;endif (Data[3] <= Data[Lid_Min])beginLid_Min = 2'd3;endendendendmodule其仿真波形如图所示:在代码仿真过程中,第二句的if为真,执行Lid_Min = 2'd1,根据阻塞赋值的特点,Lid _Min被立刻赋值为1。
在执行第三句if的时候,if (Data[2] <= Data[Lid_Min])为假,直接跳过Lid_Min = 2'd2不执行,同样也跳过Lid_Min = 2'd3不执行。
Lid_Min被最终赋值为1,这正是我们想要的结果。
另外,为了使代码看起来更简洁,我们使用for语句改写了代码:module Bubble_Up(Rst_n,Clk,Data,Lid_Min);input Rst_n;input Clk;input [5:0] Data [0:3];output [1:0] Lid_Min;reg [1:0] Lid_Min;integer i;always @(posedge Clk or negedge Rst_n)beginif (~Rst_n)beginLid_Min = 2'd0;endelsebeginfor (i = 2'd0; i <= 2'd3; i = i + 2'd1)begin。