您的位置:首页 > 其它

【转】OO无双的blocking/non-blocking执行时刻

2012-05-15 10:35 218 查看
转自OO无双大大的博客 顿首

(本文生动形象的讲述了在ieee订立的verilog标准里面blocking/non-blocking 与各种系统函数的执行顺序,读后会对每个数字设计的略糊涂人员有种醍醐灌顶之感,请进)

我用的环境还是NC+Debussy

主要说的是blocking/non-blocking 和各种仿真时会用到的一些函数和#0的执行时刻

module nb_schedule1;

reg a, b;
integer fp;

initial begin
fp = $fopen("log.txt","w");
a = 0;
b = 0;
#1;
a = 0;
b = 1;
a <= b;
b <= a;

$monitor("%0dns :\$monitor: a=%b b=%b"  , $stime, a, b);
$display("%0dns :\$display: a=%b b=%b"  , $stime, a, b);
$strobe ("%0dns :\$strobe : a=%b b=%b\n", $stime, a, b);
$fwrite(fp, "%0dns :\$fwrite : a=%b b=%b\n", $stime, a, b);
#0 $display("%0dns :#0      : a=%b b=%b"  , $stime, a, b);

#1 $monitor("%0dns :\$monitor: a=%b b=%b"  , $stime, a, b);
$display("%0dns :\$display: a=%b b=%b"  , $stime, a, b);
$strobe ("%0dns :\$strobe : a=%b b=%b\n", $stime, a, b);
$fwrite(fp, "%0dns :\$fwrite : a=%b b=%b\n", $stime, a, b);
#0 $display("%0dns :#0      : a=%b b=%b"  , $stime, a, b);

$fclose(fp);
end

initial begin
$fsdbDumpfile("nb_schedule1.fsdb");
$fsdbDumpvars(0, nb_schedule1);
end

endmodule


仿真结果

NC-Console-SimVision

# 1ns :$display: a=0 b=1
# 1ns :#0      : a=0 b=1
# 1ns :$monitor: a=1 b=0
# 1ns :$strobe : a=1 b=0
#
# 2ns :$display: a=1 b=0
# 2ns :#0      : a=1 b=0
# 2ns :$monitor: a=1 b=0
# 2ns :$strobe : a=1 b=0


log.txt

1ns :$fwrite : a=0 b=1
2ns :$fwrite : a=1 b=0


Debussy



Verilog的『stratified event queue』

在軟體的世界,程式碼基本上是一行一行地執行,也就是再同一個時間點,只有一件事情會發生;但在硬體的世界裡,電路卻可以平行執行,也就是在同一個 時間點,有很多事情可以同時發生。我們現在要在一個只能『循序處理』的軟體去『模擬』能『平行處理』的硬體,勢必有些特殊的機制才行。

在Simulator內,為了要模擬出硬體的平行處理機制,時間軸是個虛擬的時間軸,另外搭配了5個event queue,也就是說,當一個時間點的5個event queue內的所有程式碼都執行過後,時間軸才會自動加1,如此就很巧妙的『看起來』好像在同一個時間點作了很多事情,不過事實上對於軟體與CPU來說,依然是同一時間點只發生了一件事情。



1.Active Events:

大部分的Verilog程式碼都在此執行,其中包括以綠色字表示的blocking asignments以及continuous assignments (也就是用assign寫的),至於nonblocking部分,只執行RHS (Right Hand Side),也就是只執行nonblocking右測的部分,唯一用紅色表示的是本文所要討論的$display()與$fwrite(),也是屬於 Active Events,值的注意的是,在Active Events中的程式碼,若是同一個時間點,且在同一個sequential block,程式碼是依序執行,但是IEEE Verilog Standard並不保證在同一個時間點,不同的sequential block內的程式碼誰先執行,也就是說若你寫的程式碼都屬於Active Events所執行,但分屬不同的sequential block,且彼此互相依賴,就可能出現Race Condition的情形發生

當在Active Events的程式碼執行完後,會自動移出Active Events。

2.Inactive Events

當Active Events執行完後,Inactive Events的程式碼會依序變成Active Events而執行之。

Inactive Events執行的是blocking assignments且帶#0 delay,因為是#0,所以理論上還是屬於同一個時間點,只是執行順序略晚於正常的blocking assignments,這也是為什麼遇到Race Condition時,常常加上#0就會正常,這就是因為#0是屬於Inactive Events,執行順序很明顯的在Active Events之後,因此不會有在Active Events內不保證誰先誰後執行,而造成Race Condition發生。

很多人認為#0會在每個時間點的最後執行,這是個錯誤的觀念,事實上,#0只是在Inactive Events,比Nonblocking Events還要早。

3.Nonblocking Events

當Inactive Events執行完後,接著Nonblocking Event的程式碼會依序變成Active Events而執行之。

Nonblocking Events執行的是nonblocking的LHS(Left Hand Side)部分,在此我們可以明確地發現,blocking發生在Active Events,而nonblocking雖然在Active Events已經執行了RHS,但真正完成LHS是在Nonblocking Events,也就是blocking會在nonblocking之前先完成。這也是為什麼同一個時間點,nonblocking可以讀到blocking所做的改變,但是blocking卻無法讀到nonblocking的改變,因為blocking已經先執行,必須要等到下一個clk edge才能讀到nonblocking所做的改變。

除此之外,還有一個常見的迷思也可在此澄清,我們都知道寫sequential logic時要用nonblocking寫,事實上用blocking也是可以寫,只是blocking寫很容易造成Race Condition,要很小心注意其先後順序,但為什麼用nonblocking寫就不會有Race Condition呢?

理由就在於nonblocking的RHS是在Active Events,而LHS是在Nonblocking Events,儘管彼此互相依賴,但Nonblocking Events很明顯地在Active Events之後,所以執行順序可以明確地確定,不像用blocking寫時,因為在Active Events內執行順序不確定,造成結果不可預期而產生Race Condition。

4.Monitor Events

當Nonblocking Events執行完後,接著Monitor Events的程式碼會依序變成Active Events而執行之。

我們都知道$display()無法觀察nonblocking所改變的結果,必須要使用$strobe()才可以,這是為什麼呢?因 為$display()是屬於Active Events,而nonblocking完成於Nonblocking Events,明顯在Active Events之後,所以$display()跟本欄不到nonblocking所造成的改變,必須使用比Nonblocking Events更晚執行的Monitor Events中的$strobe()才可以觀察到nonblocking所改變的結果。

5.Verilog PLI Events

當Monitor Events執行完後,接著Verilog PLI Events的程式碼會依序變成Active Events而執行之。

Verilog PLI是允許你用C語言去寫一些Simulator的擴充功能,如Debussy / Verdi的$fsdbDumpfile()、$fsdbDumpvars()就是透過Verilog PLI,以前我ㄧ直搞不懂為什麼我用$display()與$fwrite()所dump的值與在Debussy / Verdi的nWave所看到的值不一樣,後來才發現原來$display()與$fwrite()是屬於Active Events,而$fsdbDumpfile()與$fsdbDumpvars是屬於Verilog PLI Events,比Nonblocking Events晚執行,這也是為什麼Debussy / Verdi可以顯示每個時間點nonblocking的值,而$display()與$fwrite()卻無法dump每個時間點的 nonblocking,彼此的結果會差1個clock。

blocking / nonblocking與$display()、$strobe()、$monitor()、$fwrite()的執行順序

了解Verilog Simulator的『stratified event queue』之後,為了解釋nb_schedule1為什麼會有這樣的執行結果,我們將所有程式依照其在Event Queue的執行順序重新排列如下圖所示:



我們可以發現,儘管nonblocking與$monitor()、$strobe()寫在$display()與$fwrite()前面,但真正在執行 時,Verilog Simulator還是會把它放在該放的Event Queue內,因為$display()與$fwrite()是放在Active Events,所以會印出a=0, b=1,接下來是Inactive Events的#0 $display(),也是顯示a=0, b=1,再過來是Nonblocking Events,執行 a <= b與b<=a,最後才是Monitor Events的$monitor()與$strobe(),因為nonblocking已經執行過,a與b的值已經改變,所以a=1, b=0,接下來因為a與b的值都不再改變,所以印出的值都不會再變。

Debussy / Verdi的nWave與blocking / nonblocking的關係



在nWave內,1 ns時,所顯示的是a=1, b=0,若你是用$display()與$fwrite()去dump資料時,會得到a=0, b=1,也就是說,$display()與$fwrite()看到的是blocking的值,而nWave看到的是nonblocking的值,因 為$fsdbDumpfile()與$fsdbDumpvars()是在Verilog PLI Events,執行點是在Nonblocking Events之後。

這也是為什麼有些工程師在寫sequential logic時,會在nonblocking加上#1,為的就是在Debussy / Verdi下看波形圖時比較好看,而Synthesizer會自動忽略nonblocking的#1,所以結果不會受影響。

Conclusion
1.因為blocking與nonblocking的關係,所以程式碼執行的順序不見的會依照我們所寫的順序,而是要依照『stratified event queue』的執行順序執行。

2.若皆屬Active Events的程式碼,在同一個時間點,在同一個sequential block內會依序執行,但在不同的sequential block內就無法保證誰會先執行,若彼此依賴有相關,就有可能造成Race Condition。

3.#0的blocking會比較晚一點執行,可以暫時解決Race Condition所造成的問題,不過這不是一個推薦的coding style,建議找出Race Condition的真正原因,而改用nonblocking去解決。

4.#0並不是最後執行,只是在Inactive Events而已,比Active Events稍晚,但還是比Nonblocking Events早。

5.Sequential logic也是可以用blocking寫,只是很容易造成Race Condition,必須很小心的安排blocking的執行順序,不是一個推薦的coding style。

6.$display()只能觀察到blocking,無法觀察到nonblocking,若要觀察nonblocking,要使用$strobe()與$monitor()。

7.$fsdbDumpfile()與$fsdbDumpvars()比$strobe()與$monitor()還晚執行,所以在Debussy / Verdi總是觀察到nonblocking的值,會比使用$display()與$fwrite()早1個clock
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: