本文版權歸本公眾號所有。
sequence介紹
熟悉UVM的朋友應該都知道,sequence的作用是將測試數(shù)據(jù)的產(chǎn)生從driver中分離出來,使得driver能專注于驅動測試數(shù)據(jù)的功能。在不同的測試用例中,將不同的sequence設置成sequencer的main_phase的default_sequence,當sequencer執(zhí)行到main_phase時,發(fā)現(xiàn)有default_sequence,那么它就會啟動這個sequence。之后sequencer將啟動的sequence產(chǎn)生的測試數(shù)據(jù)交給driver,再由driver驅動進入DUT完成仿真。
當完成一個sequence的定義后,就可以使用start任務將其啟動,即在my_case的main_phase中創(chuàng)建自定義的sequence對應的實例,然后調(diào)用sequence.start(sequencer)來啟動。除了直接啟動之外,還可以使用default_sequence啟動。一種是通過在my_case的build_phase使用uvm_config_db設置default_sequence給sequencer,另一種方式是先實例化要啟動的sequence,之后再通過default_sequence啟動。兩者的區(qū)別就在于config_db::set的第四個參數(shù),即要設置的值的獲取方式不同,前者通過type_id::get獲取,后者則直接用實例化后的變量即可。當一個sequence啟動后會自動執(zhí)行sequence的body任務。除body外,它還會調(diào)用sequence的pre_body和post_body任務。
sequence中的仲裁任務
UVM支持同一時刻在一個sequencer上啟動多個sequence,則此時sequencer起到仲裁的作用,即決定使用哪個sequence產(chǎn)生的測試用例。sequencer是根據(jù)transaction的優(yōu)先級來進行仲裁的,通常來說,優(yōu)先級越高越容易被選中。使用uvm_do和uvm_do_with宏時產(chǎn)生的transaction為默認優(yōu)先級,即-1,可以通過uvm_do_pri及uvm_do_pri_with來改變其優(yōu)先級。uvm_do_pri(uvm_do_pri_with同理)第二個參數(shù)為優(yōu)先級,用戶可以自由指定任意大于-1的整數(shù),數(shù)字越大,優(yōu)先級越高。sequencer的仲裁算法有很多種:
SEQ_ARB_FIFO,
SEQ_ARB_WEIGHTED,
SEQ_ARB_RANDOM,
SEQ_ARB_STRICT_FIFO,
SEQ_ARB_STRICT_RANDOM,
SEQ_ARB_USER
若想使得優(yōu)先級設置起作用,應該設置仲裁算法為SEQ_ARB_STRICT_FIFO或SEQ_ARB_STRICT_RANDOM。具體可在測試用例的main_phase中對sequencer進行設置。
此外,除transaction有優(yōu)先級外,sequence本身也有優(yōu)先級概念,不過其本質(zhì)還是設置該sequence產(chǎn)生的transaction的優(yōu)先級。sequence的優(yōu)先級可在啟動sequence時指定,其中第三個參數(shù)即為優(yōu)先級,如
seq0.start(env.i_agt.sqr,null,100);
除了設置優(yōu)先級外,sequencer還可以通過sequence的lock操作來改變其行為。所謂lock可以理解為一個發(fā)送transaction的請求,當該請求前面的發(fā)送請求都被仲裁執(zhí)行完畢,sequencer就開始響應lock請求,之后會連續(xù)發(fā)送發(fā)出該請求的sequence的transaction,直到unlock操作被調(diào)用。lock操作在sequence的body中被調(diào)用,當兩個sequence都試圖調(diào)用lock時,先獲得所有權的sequence在執(zhí)行完畢后才會將所有權交給另一個sequence。
在lock之上,還有優(yōu)先級更高的grab操作,不同于lock操作在被調(diào)用后要等待仲裁隊列里的請求執(zhí)行完畢才會被執(zhí)行,grab操作只要一發(fā)出立馬就會被響應,即“插隊”到仲裁隊列最前面——特殊情況是當其他sequence的lock操作正在執(zhí)行中時,grab會等該lock操作執(zhí)行完畢再執(zhí)行。grab操作同樣要ungrab來釋放所有權。當兩個sequence都試圖使用grab操作時,與同時調(diào)用lock操作的判斷規(guī)則一致。
最后,UVM還可以設置sequence,使其在一段時間內(nèi)失效不參與仲裁,這個功能可以通過重載sequence的is_relevant函數(shù)實現(xiàn)。當此函數(shù)返回1說明sequence有效,否則無效。sequence還有個函數(shù)wait_for_relevant也與有效性有關。wait_for_relevant在sequencer發(fā)現(xiàn)其啟動的所有sequence都無效時被調(diào)用,此時sequencer會等待sequence變有效,換言之,可以通過wait_for_relevant來實現(xiàn)該sequence的無效變有效受其他sequence的狀態(tài)影響。
在wait_for_relevant中,必須將使sequence無效的條件清除,否則會陷入死循環(huán),即一直沒有有效的sequence可以執(zhí)行。因此,is_relevant與wait_for_relevant一般應成對重載。
與sequence相關的宏
最基礎的sequence宏是uvm_do及其衍生,uvm_do系列宏主要包含以下八個:
uvm_do
uvm_do_pri
uvm_do_with
uvm_do_pri_with
uvm_do_on
uvm_do_on_pri
uvm_do_on_with
uvm_do_on_pri_with
從其組織形式可以看出,是uvm_do及pri,with,on等關鍵詞的排列組合,這樣有助于我們?nèi)ビ洃?。回顧一下uvm_do宏的功能,它是UVM中最常用的宏之一,它用于創(chuàng)建一個transaction的實例,將該實例隨機化,
最終將其送個sequencer with關鍵詞給uvm_do宏增加了隨機化的約束條件,pri關鍵詞則設置了發(fā)送的transaction的優(yōu)先級(在上一節(jié)有提到)。uvm_do_on宏用于顯式地指定使用哪個sequencer發(fā)送此transaction,其參數(shù)形式為`uvm_do_on(SEQ_OR_ITEM,SEQR),第一個參數(shù)是transaction的指針,第二個是sequencer的指針。當使用uvm_do時,它實際等價于將uvm_do_on的第二個參數(shù)設置為了默認的sequencer,即此sequence啟動時為其指定的sequencer。uvm_do_on_pri等在此基礎上加上各關鍵詞的功能,與uvm_do_pri與uvm_do之間的關系類似,因此uvm_do系列的宏本質(zhì)上都是uvm_do_on_pri_with的特殊形式。
除了使用uvm_do宏自動產(chǎn)生,隨機化并發(fā)送transaction之外,還可以通過uvm_create宏與uvm_send宏來實現(xiàn)這個過程。uvm_create宏的作用是實例化transaction,當一個transaction被實例化后,可以對其做更多的處理(如隨機化),處理完畢后使用uvm_send宏發(fā)送出去。當然,也可以不用uvm_create而直接使用new函數(shù)來進行實例化。uvm_send也可以增加pri關鍵詞以設置其優(yōu)先級。
此外,還可以將隨機化處理與發(fā)送合并為uvm_rand_send系列宏來實現(xiàn)。這個宏的使用前提是transaction已經(jīng)被分配了空間,即已經(jīng)實例化了。uvm_rand_send宏可增加pri,with關鍵字。設計uvm_rand_send系列宏的意義在于,如果一個transaction占用的內(nèi)存很大,那么很可能希望前后兩次發(fā)送的transaction都使用同一塊內(nèi)存,只是內(nèi)容不同,這樣子比較節(jié)約內(nèi)存空間。
以上都是使用宏來完成transaction發(fā)送相關的工作,但這樣子隱藏了具體的實現(xiàn)細節(jié)。不使用宏產(chǎn)生transaction的方式主要依賴于兩個任務,start_item與finish_item。在使用這兩個任務之前,必須要先實例化transaction后才可以調(diào)用。完整使用如上兩個任務構建一個sequence的代碼如下:
virtual task body();
repeat(10) begin
tr = new("tr");
start_item(tr);
finish_item(tr);
end
endtask
對transaction進行隨機化的操作可放在實例化之后,finish_item之前的任意位置。start_item和finish_item都可以在調(diào)用時指定優(yōu)先級。
了解了uvm_do宏實現(xiàn)的細節(jié)之后,為了增加uvm_do系列宏的靈活度,UVM提供了三個接口:pre_do,mid_do與post_do。
pre_do是一個任務,在start_item中被調(diào)用,是start_item返回前執(zhí)行的最后一行代碼,在它執(zhí)行完成后才開始對transaction進行隨機化。pre_do有一個1bit參數(shù),用于表明uvm_do是對一個transaction還是一個sequence進行操作(詳見下一節(jié)sequence的嵌套)。
mid_do是一個函數(shù),在finish_item最開始被調(diào)用,在執(zhí)行完此函數(shù)后才會進行finish_item中的其他操作。mid_do有一個參數(shù),表示正在操作的sequence或transaction的指針,但其類型是uvm_sequence_item,需要通過cast轉換成目標類型。
post_do也是一個函數(shù),在finish_item中最后一行代碼被調(diào)用。post_do也有一個參數(shù),與mid_do類似。
sequence進階應用
在一個sequence的body中,除了可以使用uvm_do產(chǎn)生transaction外,還可以啟動其他的sequence,這就是sequence的嵌套。嵌套的方式也非常簡單,直接在新的sequence的body任務中調(diào)用定義好的sequence即可,如下所示:
virtual task body();
SEQ0 seq0;\定義化seq0
SEQ1 seq1;\定義化seq1
repeat(10) begin
`uvm_do(seq0);
`uvm_do(seq1);
end
endtask
在上述代碼中,使用了uvm_do宏。uvm_do宏的第一個參數(shù)可以是transaction的指針,此時其調(diào)用start_item和finish_item;該參數(shù)也可以是sequence的指針,此時其調(diào)用該sequence的start任務。除了uvm_do外,前述介紹的uvm_send,uvm_rand_send,uvm_create宏等,其第一個參數(shù)均可以是sequence的指針。
與transaction類似,sequence中也可以加入rand修飾的變量,用以進行對其產(chǎn)生的transaction進行約束。加入了rand變量的sequence可以通過uvm_do_with等宏添加約束條件。不過有一個需要注意的地方,在sequence中定義rand類型變量以向產(chǎn)生的transaction傳遞約束時,變量的名字一定要與transaction中相應字段的名字不同。這是因為如果名字一樣,編譯器就無法正確識別該變量究竟在sequence中還是在transaction中,會產(chǎn)生bug。
一般來說,嵌套sequence能正確的啟動的條件之一是嵌套的所有sequence產(chǎn)生的所有transaction類型需與sequencer能發(fā)送的transaction類型一致。不過有一種方法將兩個不同的transaction交給同一個sequencer處理,那就是將sequencer和driver能夠接受的數(shù)據(jù)類型設置為uvm_sequence_item。
class my_sequencer extends uvm_sequencer #(uvm_sequence_item);
class my_driver extends uvm_driver #(uvm_sequence_item);
這樣子,在driver接收數(shù)據(jù)時,通過cast將其轉換為不同的transaction類型,即可實現(xiàn)一個sequencer/driver發(fā)送多種類型transaction的功能了。
考慮一種特殊情況,即sequence中產(chǎn)生的transaction受對應的sequencer中變量的約束,此時該如何在sequence中獲取該變量值呢?UVM內(nèi)建了一個宏uvm_declare_p_sequencer(SEQUENCER),這個宏聲明了一個SEQUENCER類型的成員變量,在定義sequence時,使用這個宏聲明對應sequencer的類型如下
class case0_sequence extends uvm_sequence #(my_transaction);
my_transaction my_trans;
`uvm_object_utils(case0_sequence)
`uvm_declare_p_sequencer(my_sequencer)
endclass
這樣UVM就會自動把m_sequencer(sequence默認sequencer變量,為uvm_sequencer_base類型)轉換為my_sequencer類型,這個過程在pre_body()之前就完成了。這樣在sequence中可以直接使用成員變量p_sequencer,從而獲取my_sequencer中設置的變量值。這個概念比較難理解,讀者可以參考《UVM實戰(zhàn)》6.4.4節(jié),結合具體代碼理解。
由于在同一個項目中各sequence通常都是類似的,因此可以將很多公用的函數(shù)或者任務寫在base_sequence中,其他sequence都從此類派生。sequence是支持派生與繼承的。同時對于使用了uvm_declare_p_sequencer的base_sequence,在派生的sequence中不需要再次聲明,p_sequencer直接成為新的sequence的成員變量。
參考文獻:https://zhuanlan.zhihu.com/p/349791759