未闻verilog---阻塞赋值与非阻塞赋值
阻塞赋值与非阻塞赋值概念
阻塞赋值和非阻塞赋值都属于过程赋值,所谓过程赋值语句就是只能在initial 和always 语句块中的复制语句。赋值对象只能是寄存器变量类型。右边的表达式可以是任意操作符的表达式。
阻塞赋值
阻塞赋值的语法如下
寄存器变量 = 表达式;
所谓阻塞赋值具有两层含义:
- 右边表达式(RHS,right hand side)的计算和对左边寄存器变量的赋值是一个统一的原子操作中的两个动作。这两个动作之间不能插入其他操作
- 如果多个阻塞赋值语句顺序出现在begin...end块中,前面的语句在执行时将完全阻塞后面的语句,直到前面语句的赋值完成以后才会执行下一句的右边表达式计算。
非阻塞赋值
非阻塞赋值的语法如下
寄存器变量 <= 表达式;
所谓非阻塞赋值同样具有两层含义:
- 在执行该语句时,首先计算右边的表达式,然后并不立刻对左边的变量赋值。由于这个赋值操作在当前仿真时间事件队列中的优先级比较低,因此将赋值推迟到当前仿真时刻的后期运行。(后期有多后呢?这要看后面的分层事件列的内容)。
- 如果多个非阻塞赋值语句顺序出现在begin...end语句中,前面语句的执行并不会阻塞后面语句的执行。
深入理解
阻塞赋值
阻塞赋值的计算RHS值和修改LHS值不能被其他verilog语句中断,所以阻塞赋值的阻塞是这个意思,他阻塞了其他语句的运行,直到这条语句赋值完成。(也有例外,比如在阻塞赋值=后面跟时序控制,比如A=#2 B & C;
,这条语句阻塞了相同块内其他语句的运行,但没有阻塞其他块的语句的运行。)
阻塞赋值属于活跃事件队列中,由于活跃事件队列中的事件运行顺序具有随机性,所以就容易发生竞争与冒险。
比如:两个变量对同一个变量进行read和write操作(不在同一块内,没有begin...end的约束),就容易发生竞争。
如果两个变量对同一个变量同时进行write操作(不在同一块内,没有begin...end的约束),也容易发生竞争。
非阻塞赋值
非阻塞赋值在time-step的开始先计算RHS的值,在time-step的末尾更新LHS的值。
非阻塞赋值计算RHS的值属于活跃事件,在仿真时间的开始就开始运行,而非阻塞赋值的更新LHS的事件属于非阻塞赋值更新事件队列中的事件,只有当所有的活跃事件都运行完之后才开始运行非阻塞赋值的更新事件,所以在time-step的末尾更新LHS的值。
自触发
一般来说,always块不能自己触发自己。
对于阻塞赋值的情况:
module osc1(output reg clk);
initial #10 clk =0;
always @(clk) #10 clk = ~clk;
endmodule
这个振荡器使用阻塞赋值,阻塞赋值在计算RHS和更改LHS时不能被中断。这个阻塞赋值在这个always块对clk的变化又变成敏感之前完成,这样在这个always块对clk的变化又变成敏感之时,阻塞赋值已经完成(阻塞赋值在always块结束之前完成了计算与更新),所以这个always块不会触发自己。
对于非阻塞赋值的情况:
module osc2(output reg clk);
initial
#10 clk =0;
always @(clk)
#10 clk <= ~clk;
这个振荡器使用非阻塞赋值,在第一个@(clk)触发后,就计算非阻塞赋值的RHS,然后把更新LHS调度到非阻塞赋值更新队列(NBAU_EQ)。在非阻塞赋值更新队列被激活之前,这个always块对clk的变化又变成敏感。这样在同一个time-step当LHS发生更改时,@(clk)又被触发,所以这个always块可以触发自己。(非阻塞赋值的RHS计算在always块结束之前。非阻塞赋值的LHS更新发生在always块结束之后)
当clk = ~clk 这个blocking assign event 被执行完,为什么不会触发下一次@(clk)。这是因为当always被触发后,要当always内的event做完,才会重新保持对敏感列表的敏感)。阻塞赋值时,always对clk不敏感,赋值完,clk变化的事件不存在,因此不能自激触发。而对于非阻塞赋值,第一次@clk被触发时,always块内会做的是把 对RHS估值的事件加入active event queue,然后就恢复对clk的敏感,而LHS的noblocking assign event是当RHS估值的事件完成后加入相应的事件队列的,(仿真模型代码中可以看的出来)当执行到LHS非阻塞赋值事件的时候,always对clk敏感,又会重复上述事件。
阻塞赋值执行完,always块才结束;非阻塞赋值在always块执行完后才执行(估右值在其前,刷新左值在其后)
建议
原则一:时序电路建模时,用非阻塞赋值
(阻塞赋值更符合组合电路的行为,非阻塞赋值更符合时序电路的行为)
原则二:锁存器电路建模时,用非阻塞赋值
(用阻塞赋值一方面可能功能就不对,另一方面可能会导致综合后的电路不是想要的电路)
原则三:用always块描述组合逻辑时,应采用阻塞赋值语句
(也可以用非阻塞赋值,但是可能导致不能立刻得到想要的结果,另一方面可能会有多次参数传递,降低仿真器的性能)
原则四:在同一个always块中描述时序和组合逻辑混合电路时,用非阻塞赋值
(要遵循时序电路的逻辑,所以在一个块里用非阻塞赋值,当然也可以阻塞赋值写在一个块,非阻塞赋值写在一个块里)
原则五:不要在同一个always块中同时使用阻塞和非阻塞赋值
(verilog允许同时使用,但是不建议,当对同一个变量即进行阻塞赋值又进行非阻塞赋值时,会产生综合错误的结果)
原则六:严禁在多个always块中对同一个变量赋值
(会产生竞争冒险)
参考引用
《verilog编程艺术》
Verilog -- 阻塞与非阻塞的仿真与综合 - love小酒窝 - 博客园 (cnblogs.com)
Verilog 自触发always块浅析 - zslhutu的日志 - EETOP 创芯网论坛 (原名:电子顶级开发网)
(18条消息) 阻塞赋值和非阻塞赋值学习笔记_sdbzlh的专栏-CSDN博客
通过Verilog事件处理机制实现阻塞与非阻塞赋值的区分-电子发烧友网 (elecfans.com)