問題:在進程等待event觸發的同一時間步長內,若該event觸發,則不一定能等到該event
E文原文:http://www.deepchip.com/items/0466-07.html
(轉載請註明出處,謝謝!seabeam)verilog通過event數據類型提供一種基本的進程同步機制,使用這種機制會遇到兩個問題。第一個問題也許一開始不會被當做問題,但是過了幾年你會意識到這真是個問題:許多工程師甚至不知道sv中還有event,根本沒想到去用它。有個工程師使用了N年的verilog後最近纔打算和他的team參加verilog培訓班。當上到event這節,他問這是不是sv裏的新玩意,答案是它早就在Verilog裏。他驚呆了:“以前咋沒人告訴我呢?”
第二點,說點有現實意義的,event事件觸發在仿真時很容易產生競爭條件,看看下面這個例子:
module event_example1;
event get_data, send_data; // handshaking flags
initial -> get_data; // trigger get_data event at time zero
always @(get_data) begin // wait for a get_data event
... // do code to get data
... // when done, trigger send_data
-> send_data; // sync with send_data process
end
always @(send_data) begin // wait for a send_data event
... // do code to send data
... // when done, trigger get_data
-> get_data; // sync with get_data process
end
endmodule
這個簡單的例子裏有兩個always塊,使用event同步做了一個簡單的握手模型,當一個塊結束時另一塊開始工作。問題在於仿真開始的0時刻,兩個always塊都處於活動狀態,如果initial比get_data的always塊先激活,那這個module就別想開工了。怎麼辦呢?verilog裏只有一條路:延遲initial裏的event觸發時間,至少到get_data的always塊被激活之後:
initial #0 -> get_data; // start handshaking at time 0, but after all
// procedural blocks have been activated
使用#0延遲觸發get_data,保證在0時刻get_data的always塊先跑起來。但是#0本身就是個問題,在verilog裏是個容易被濫用且不能保證在一個給定的時間步長裏,加了#0就真的會在所有語句執行完畢
後執行(譯者注:例如工程是個龐然大物,在不同的結構裏都存在#0語句,那麼他們之間也會互相競爭)。所以許多老師教導我們不要用#0,要用非阻塞賦值,或者預先確定順序的event.除了event數據
類型,不使用#0是個好的guideline.但是在verilog裏,沒法將event觸發做成非阻塞event隊列。
這時候sv帶了兩種解決方案來救世了,現在可以和#0道別了。
方案1:操作符->>,它可以讓event觸發排成非阻塞隊列。在本節的例子裏,避免0時刻競爭條件的同時徹底拋棄了#0.將get_data event放入非阻塞隊列使其觸發前保證過程塊處於激活狀態(譯者注:->>操作符不能出現在class裏,只能存在於module中)。
initial ->> get_data; // start handshaking at time 0 nonblocking
// queue, after all procedural blocks have
// been activated
方案2:sv提供另一種方法以滿足更多情況的需求。這種方法利用event內建的trigger屬性使觸發在整個時間步長內可見,而不是觸發的那一瞬間。
module event_example2 ( ... );
event get_data, send_data; // handshaking flags
initial -> get_data; // trigger get_data event at time zero
always begin
wait(get_data.triggered) // wait for a get_data event
... // do code to get data
... // when done, trigger send_data
-> send_data; // sync with send_data process
end
always @(send_data) begin // wait for a send_data event
// could have used wait(send_data.triggered) here also, but it is
// not needed since there is no race condition between the two
// always blocks
... // do code to send data
... // when done, trigger get_data
-> get_data; // sync with get_data process
end
endmodule
當get_data觸發時,wait(get_data.triggered)返回爲真。event觸發行爲發生在wait語句激活前後都無關緊要。在上面的例子中,如果initial塊先於第一個always塊激活,那麼激活行爲在第一個always block激活時仍然可見,而且wait(get_data.triggered)語句也會被執行。