Verilog testbench的initial塊中阻塞與非阻塞賦值問題
問題描述
在testbench的編寫中經常要做的就是在initial塊中對一些信號變化進行描述。
比如希望信號start在仿真開始后第10個周期上升沿置為高電平。
對於仿真時鍾一般都會這么寫:
always #1 clk = ~clk;
- 如果初始化clk = 0,那么實際上start應該在 #21;start=1; 也就是十個半周期后置高。
- 如果初始化clk = 1,那么實際上start應該在 #20;start=1; 也就是准確的十個周期后置高。
這種寫法實際上不符合實際電路中的運行規則。
如果start在上層模塊是通過其他FF給出的,則這樣的寫法往往會導致數據提前一拍,因為#20;start=1;的寫法是跟時鍾上升沿無關的,因此在該處時鍾上升沿實際上和start的值同時改變,而我們知道,實際上FF的原理是一般都是基於正負latch,其采樣和保持都需要一定的延時(clk-Q),所以用clk去驅動FF時,FF的Q端數據一般都是在上升沿之后的一段延時后才改變。
這也是為什么verilog中要使用“<=”非阻塞賦值的一個因素,因為非阻塞賦值契合了實際電路中的這一特性,如果該FF之后又級聯了一個FF,則在這個clk上升沿,如果不考慮時鍾skew,那么后面級聯的FF實際上采樣到的數據還是前級FF賦值之前的數值,非阻塞賦值可以保證這一特性。
- 阻塞:在本語句中“右式計算”和“左式更新”完全完成之后,才開始執行下一條語句(遇到等式時堵住值直到更新才釋放);
- 非阻塞:當前語句的執行不會阻塞下一語句的執行(遇到等式立馬就把右邊值給左邊,不等其更新)。
所以在tb里initial塊中一般使用
@ (posedge clk);
start<=1;
這種寫法來保證非阻塞的特性,也就是在時鍾上升沿之后reg值才變化。實際上沒有@ posedge也可以,只要賦值語句是非阻塞的,則等式的執行實際上相對當前的時間觸發會有一定的延時,而@ posedge clk可以方便的間隔一定時間知道clk的上升沿到來。
下面是一段測試程序,以便更好的理解兩者的差別:
module test();
reg clk;
reg req0;
reg req1;
reg test_sig;
always #1 clk = ~clk;
initial begin
clk = 0;
req0 = 0;
req1 = 0;
test_sig = 0;
#10;
@ (posedge clk);
req0 <= 1;
req1 <= req0;
repeat (1) @ (posedge clk)
req0 <= 1;
req1 <= req0;
#10;
req0 = 0;
req1 = req0;
end
always@(posedge clk) begin
if(req0==1) test_sig <= 1;
else test_sig <= 0;
end
initial begin
$fsdbDumpvars();
$fsdbDumpMDA();
$dumpvars();
#100 $finish;
end
endmodule

其中,test_sig依賴req0的值。可以發現,當使用@ (posedge clk);配合“<="賦值時,test_sig的值在req0變化的下一周期才改變,req1也是如此,符合時序電路的規范。而下面使用"#"延時和阻塞賦值的語句則導致test_sig、req1跟着req0的值同時改變。
再來看一個例子:


另外,如果把延時信息放到賦值的前面,會發生什么呢?
首先,可以知道的是對於阻塞賦值,不管放在哪里都是一樣的,都是串行執行,所以延時會累加。
那么,對於非阻塞賦值的情況呢?下圖進行了實驗:
