未聞verilog---分層事件佇列
分層事件佇列
當我們在談到非阻塞賦值時都會說非阻塞賦值不對LHS立刻進行賦值,它在time-step的最後進行賦值。但是time-step的最後又是什麼時候?就需要討論Verilog的分層事件佇列。
Verilog描述了電路的行為,是並行進行的,在模擬的時候我們把每一條語句每一個程式碼塊都看作並行執行的,但是對於模擬器來說,他執行在計算機上,計算機在執行模擬器的程式碼時是序列進行的,所以對於Verilog程式碼來說,他的執行有前有後,到底哪一條語句先執行,哪一條語句後執行,都是需要按照一定的規則的,這些規則就是Verilog模擬的參考模型和事件佇列。
verilog模擬的參考模型
Verilog模擬時在進行事件排程時按照下面的參考模型進行排程(下面是將0延時部分拿去的簡化版本)
“T”代表當前的模擬時間
參考模型如下:
while(佇列中有事件需要處理){ //以下是啟用事件的過程 if(沒有活躍事件) { if(有非活躍事件) {啟用所有的非活躍事件;} else if(有非阻塞賦值的更新事件) {啟用所有非阻塞賦值的更新事件;} else if(有monitor事件) {啟用所有monitor事件} else {將T推進到下一個事件時間; 啟用時間T的所有非活躍時間;} } //以下是執行時間的過程 E=任何活躍事件; if(E是一個更新事件) {更新需修改物件; 將產生的計算事件增加進事件佇列;} else //上面是賦值,下面是計算右邊得到右值 {/* 應該是一個計算事件 */ //先計算得到右值,再更新左值 計算這個程序; 將計算產生的更新事件加入事件佇列;} }//end while
從上面可以看出,先執行活躍事件,活躍事件執行完之後再執行非活躍事件,再執行非阻塞賦值的更新事件,再執行所有的monitor事件,如果當前時刻的所有事件都執行完,推進模擬事件T前進。
對於活躍事件來說,先進行計算事件的進行,再進行更新事件的進行。
分層事件佇列
模擬器首先按照模擬時間對事件進行排序,而在當前模擬時間裡,則按照事件的優先順序進行排序。
活躍事件是優先順序最高的事件。在活躍事件之間,它們執行的順序是隨機的,阻塞賦值(=),連續賦值(assign),以及非阻塞賦值的右式計算等,都屬於活躍事件。
當活躍事件全部執行完時,系統會按照模擬參考模型,將其他佇列中的事件啟用為活躍事件,加入活躍事件佇列中執行。事件是根據一定的規則加入到5個區域中任何一個的,但是隻從其中的活躍事件佇列區域離開。
其他將來模擬時間的所有事件(比如延時後賦值計算)暫時存放在將來事件佇列。當模擬器前進到某個時刻,該時刻的所有事件也就被分類到當前模擬時間事件佇列中。模擬時刻未到的事件仍然留在將來事件佇列中。
事件的啟用會觸發其他事件:
活動事件如阻塞賦值和連續賦值,可以觸發額外的賦值和過程塊,從而導致更多的活動時間和非阻塞賦值更新事件在同一個time-step中被排程。在這種情況下,新的活躍事件將會在啟用非阻塞賦值更新事件之前被執行。
啟用非阻塞賦值事件意味著取出所有的非阻塞賦值的更新事件,然後把它們放入到活躍事件佇列。當這些啟用的事件被執行的時候,他們還會觸發其他的程序,導致更多的活躍事件,導致更多的非阻塞賦值更新事件在當前的time-step被排程。當前的time-step的活動持續進行,直到當前的time-step的所有事件被執行完,而且不再有能夠導致更多事件被排程的程序觸發。就此所有的$monitor
和$strobe
命令顯示他們各自要輸出的數值,然後模擬時間T可以前進。如果在當前的模擬時間裡還有活動時間和非阻塞賦值更改事件,那麼模擬時間不會向前。
模擬佇列的確定性與不確定性
確定性
模擬佇列的確定性在於begin...end塊中的語句應該按照他們在塊中的順序執行,因為模型中其他的程序(比如@,#,或者wait)在執行begin...end塊中的語句時,中間可能會被掛起,轉而執行其他程序,但是最後begin...end塊中的語句必須按照他們在塊中呈現的順序執行。
對於阻塞賦值,語句的執行就是在begin...end塊中按從上到下的順序執行,但是對於非阻塞賦值來說,舉個例子
initial
begin
a <= 0;
a <= 1;
end
當執行這個塊的時候,第一條語句a <= 0;
的右值計算部分先被放入活躍佇列,更新部分被放入非阻塞賦值更新佇列,第二條語句a <= 1;
的右值計算部分之後被放入活躍佇列,更新部分被放入非阻塞賦值更新佇列,第一條語句的更新事件排在第二條語句之前,則當把更新事件從佇列中被排程出來時,先排程第一條語句的更新事件,a被賦值為0,再把第二條語句的更新事件排程出來,a的值被賦值為1。
所以模擬佇列的確定性就在於統一begin...end塊中語句執行的順序,在前面的先執行,在後面的後執行。
不確定性
模擬佇列的不確定性在於活躍佇列語句的執行沒有順序規定,活躍佇列語句執行的順序是隨機的。
舉個例子,當兩個程序對一個變數同時有read又有write可能會發生競爭。
always @(posedge clk)
x = y;
always @(posedge clk)
y = z;
由於阻塞賦值位於活躍佇列,上面兩條阻塞賦值語句沒有begin...end塊的約束,他們在活躍佇列中的執行順序是隨機的,是不確定的,當上面的語句先執行時,x的值為y原來的值,當下面的語句先執行時,x的值為z的值。所以這種執行順序的不確定性導致了競爭。
如果把上面的阻塞賦值轉換為非阻塞賦值,就可以正常運行了。因為非阻塞賦值先進行右值的計算,這是要賦值給x和y的值就已經確定,之後在非阻塞賦值更新事件啟用時,x被賦予y原來的值,y被賦予z的值,沒有發生競爭。(這就是為什麼建議在時序邏輯中採用非阻塞賦值)
如果兩個程序同時對一個變數進行write
always @(posedge clk)
x <= y;
always @(posedge clk)
x <= z;
不管採用阻塞賦值還是非阻塞賦值,都會出現執行順序的不確定(最後x的值為y還是z是不確定的),會導致競爭。(所以我們不建議在不同的塊中同時對一個變數進行賦值)
模擬佇列的不確定性還來源於行為塊中沒有時序控制(例如@,#或wait)的語句不用必須在一個事件中連續執行完。在執行一條語句時,模擬器可以掛起執行,把部分執行完的語句作為掛起事件放到事件佇列中,這麼做就是允許程序交替執行,但是交替執行的順序是不確定的,而且不再使用者的控制下。
比如計算表示式和更新線網可能是交替的,執行順序的隨機性就會導致競爭的發生。
assign p =q;
initial
begin
q = 1;
#1 q = 0;
$display(p);
end
那麼display的結果是0還是1是不確定的,當執行完#1 q=0;
這條語句時,會導致對p的更新事件的發生,那麼接下來執行assign p=q;
還是執行diaplay是不確定的,他們都處於活躍事件佇列,那麼他們執行順序的不確定就會導致最後結果的不同。
參考
《Verilog程式設計藝術》
《輕鬆成為設計高手》