在Verilog HDL最难弄明白的语法结构中,“非阻塞赋值”要算一个,甚至是一些很有经验的工程师也不完全明白非阻塞赋值在IEEE标准的仿真器里是怎样被调度的,以及什么时候用非阻塞赋值,为什么用非阻塞赋值。这篇文章将介绍怎样设定非阻塞赋值和阻塞赋值,给出编码指导方针和编码风格细节,以得到正确的可综合逻辑并避免仿真竞争。
1.0简介
众所周知的逻辑建模方针是:
a) 在always 块里用阻塞赋值产生组合逻辑;
b) 在always 块里用非阻塞赋值产生时序逻辑;
但是为什么?一般的回答是:那只是关于仿真的,即使不遵照上面的规则也照样可以产生正确的综合结果。但问题是综合前的仿真结果也许会与综合后的电路行为仿真不匹配。要明白上述方针背后的原因,就必须明白非阻塞赋值和阻塞赋值两者的功能和时序安排。本文将详细描述有关问题。文章里将用到两个缩写形式:RHS(right-hand-side)和LHS(left-hand-side),前者指等号右边的表达式或变量,后者指等号左边的表达式或变量。
2.0 Verilog竞争状态
IEEE Verilog Standard定义了“执行顺序确定的描述”和“执行顺序不确定的描述”。当两个或多个不同的描述被调度于同一仿真时间步内执行时,如果不同的执行顺序会得到不同的结果,就会产生竞争,这就是IEEE Verilog Standard定义的竞争状态。为了避免竞争状态,理解Verilog对非阻塞赋值和阻塞赋值的调度方式是非常重要的。
3.0 阻塞赋值
阻塞赋值的操作符是等号“=”。阻塞赋值因其行为而得名:求出RHS表达式的值并完成赋值行为,这个过程不能被其它的Verilog描述打断。“阻塞”就是说在当前的赋值行为完成之前阻塞其它的赋值行为。一个例外是在阻塞赋值操作符的RHS带有时延的阻塞赋值,这种情况下在此操作延时等待时可以执行其它赋值操作,但这是一种很不好的代码风格。
阻塞赋值可以看作一个单步过程:求出RHS的值并赋给LHS,不允许其它描述打断此操作。在同一个always块里面,阻塞赋值行为将“阻塞”后面的赋值操作直到本次赋值操作结束。阻塞赋值的一个问题是:当一个程序块中的阻塞赋值描述的RHS变量同时是另一个程序块中的阻塞赋值描述的LHS变量,并且两个等式的执行被调度在同一仿真步长时间内执行(比如同一个时钟上升沿),那么会出现竞争状态,这样的情况下其执行次序将是未知的。为了说明这种情况,请看例一的Verilog代码:
module fbosc1 (y1, y2, clk, rst);
output y1, y2;
input clk, rst;
reg y1, y2;
always @(posedge clk or posedge rst)
if (rst) y1 = 0;
else y1 = y2;
always @(posedge clk or posedge rst)
if (rst) y2 = 1;
else y2 = y1;
endmodule
Example 1 - Feedback oscillator with blocking assignments
依据IEEE Verilog Standard,这两个always块可以以任意的次序调度。如果在一个复位信号后第一个块先被执行,结果是y1和y2都被赋值1;如果在一个复位信号后第二个块先被执行,结果是y1和y2都被赋值0。这个例子清楚地展示了一个Verilog竞争状态。
4.0 非阻塞赋值
非阻塞赋值的操作符是小于等于号“<=”。非阻塞赋值同样因行为而得名:在一个时间步的开始求RHS表达式的值并在这个时间步结束时用RHS的值替换LHS;在求RHS表达式和替换LHS变量的中间时间段,其它的Verilog语句和非阻塞赋值操作可以被执行。也就是说,非阻塞赋值并不阻止其它的Verilog语句的求解。非阻塞赋值可以看作一个二步过程:1. 在时间步开始时求RHS的值;2. 在时间步结束时替换LHS的值;非阻塞赋值为寄存器数据类型而设,所以只允许在程序块中出现,比如initial块和always块,不允许持续赋值。请看例二的Verilog代码:
module fbosc2 (y1, y2, clk, rst);
output y1, y2;
input clk, rst;
reg y1, y2;
always @(posedge clk or posedge rst)
if (rst) y1 <= 0;
else y1 <= y2;
always @(posedge clk or posedge rst)
if (rst) y2 <= 1;
else y2 <= y1;
endmodule
Example 2 - Feedback oscillator with nonblocking assignments
依据IEEE Verilog Standard,这两个块可以以任意的次序执行。在一个复位信号后,不管哪一个块先被执行,在时间步的开始两个RHS表达式同时被求值,在时间步的结束同时替换LHS变量的值。在使用者的角度看,这两个非阻塞赋值是并行执行的。
5.0 Verilog编码指导仿真
在对非阻塞赋值和阻塞赋值作进一步的解释和举例之前,现列举八条有帮助的指导方针。这些方针有助于正确地用Verilog对硬件建模和仿真,谨遵这些方针可以避免90-100%的Verilog设计者可能遇到的Verilog竞争状态。
l 对时序逻辑建模时,使用非阻塞赋值;
l 对锁存器建模时,使用非阻塞赋值;
l 用always块对组合逻辑建模时,使用阻塞赋值;
l 同一个always块中既有组合逻辑又有时序逻辑时,使用非阻塞赋值;
l 不要在同一个always块中混合使用阻塞赋值和非阻塞赋值;
l 不要在多于一个always块中对同一个变量进行赋值;
l 使用$strobe以显示已被非阻塞赋值的值;
l 不要使用零时延#0赋值;Verilog新手一定要记住并遵守这些方针直到完全弄明白了它们的内在机理,这将会帮助你避免“death by Verilog!”。