1. 程式人生 > 其它 >未聞verilog---分層事件佇列

未聞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程式設計藝術》
《輕鬆成為設計高手》