本節書摘來自華章社群《uvm實戰》一書中的第2章,第2.4節uvm的終極大作:sequence,作者 張 強,更多章節内容可以通路雲栖社群“華章社群”公衆号檢視
2.4 uvm的終極大作:sequence
2.4.1 在驗證平台中加入sequencer
sequence機制用于産生激勵,它是uvm中最重要的機制之一。在本書前面所有的例子中,激勵都是在driver中産生的,但是在一個規範化的uvm驗證平台中,driver隻負責驅動transaction,而不負責産生transaction。sequence機制有兩大組成部分,一是sequence,二是sequencer。本節先介紹如何在驗證平台中加入sequencer。一個sequencer的定義如下:
代碼清單 2-58
在加入sequencer後,整個uvm樹的結構變成如圖2-9所示的形式。

2.4.2 sequence機制
在加入sequencer後,整棵uvm樹如圖2-9所示,驗證平台如圖2-2所示,是一個完整的驗證平台。但是在這個驗證平台框圖中,卻找不到sequence的位置。相對于圖2-2所示的驗證平台來說,sequence處于一個比較特殊的位置,如圖2-10所示。
sequence不屬于驗證平台的任何一部分,但是它與sequencer之間有密切的聯系,這點從二者的名字就可以看出來。隻有在sequencer的幫助下,sequence産生出的transaction才能最終送給driver;同樣,sequencer隻有在sequence出現的情況下才能展現其價值,如果沒有sequence,sequencer就幾乎沒有任何作用。sequence就像是一個彈夾,裡面的子彈是transaction,而sequencer是一把槍。彈夾隻有放入槍中才有意義,槍隻有在放入彈夾後才能發揮威力。
除了聯系外,sequence與sequencer還有顯著的差別。從本質上來說,sequencer是一個uvm_component,而sequence是一個uvm_object。與my_transaction一樣,sequence也有其生命周期。它的生命周期比my_transaction要更長一些,其内的transaction全部發送完畢後,它的生命周期也就結束了。這就好比一個彈夾,其裡面的子彈用完後就沒有任何意義了。是以,一個sequence應該使用uvm_object_utils宏注冊到factory中:
代碼清單 2-63
在如上的代碼中,一個最顯著的特征是使用了while(1)循環,因為driver隻負責驅動transaction,而不負責産生,隻要有transaction就驅動,是以必須做成一個無限循環的形式。這與monitor、reference model和scoreboard的情況非常類似。
通過get_next_item任務來得到一個新的req,并且驅動它,驅動完成後調用item_done通知sequencer。這裡為什麼會有一個item_done呢?當driver使用get_next_item得到一個transaction時,sequencer自己也保留一份剛剛發送出的transaction。當出現sequencer發出了transaction,而driver并沒有得到的情況時,sequencer會把保留的這份transaction再發送出去。那麼sequencer如何知道driver是否已經成功得到transaction呢?如果在下次調用get_next_item前,item_done被調用,那麼sequencer就認為driver已經得到了這個transaction,将會把這個transaction删除。換言之,這其實是一種為了增加可靠性而使用的握手機制。
在sequence中,向sequencer發送transaction使用的是uvm_do宏。這個宏什麼時候會傳回呢?uvm_do宏産生了一個transaction并交給sequencer,driver取走這個transaction後,uvm_do并不會立刻傳回執行下一次的uvm_do宏,而是等待在那裡,直到driver傳回item_done信号。此時,uvm_do宏才算是執行完畢,傳回後開始執行下一個uvm_do,并産生新的transaction。
在實作了driver後,接下來的問題是:sequence如何向sequencer中送出transaction呢?前面已經定義了sequence,隻需要在某個component(如my_sequencer、my_env)的main_phase中啟動這個sequence即可。以在my_env中啟動為例:
代碼清單 2-66
相比于get_next_item,try_next_item的行為更加接近真實driver的行為:當有資料時,就驅動資料,否則總線将一直處于空閑狀态。
2.4.3 default_sequence 的使用
在上一節的例子中,sequence是在my_env的main_phase中手工啟動的,作為示例使用這種方式足夠了,但是在實際應用中,使用最多的還是通過default_sequence的方式啟動sequence。
使用default_sequence的方式非常簡單,隻需要在某個component(如my_env)的build_phase中設定如下代碼即可:
代碼清單 2-69
這是除了在top_tb中通過config_db設定virtual interface後再一次用到config_db的功能。與在top_tb中不同的是,這裡set函數的第一個參數由null變成了this,而第二個代表路徑的參數則去除了uvm_test_top。事實上,第二個參數是相對于第一個參數的相對路徑,由于上述代碼是在my_env中,而my_env本身已經是uvm_test_top了,且第一個參數被設定為了this,是以第二個參數中就不需要uvm_test_top了。在top_tb中設定virtual interface時,由于top_tb不是一個類,無法使用this指針,是以設定set的第一個參數為null,第二個參數使用絕對路徑uvm_test_top.xxx。
另外,在第二個路徑參數中,出現了main_phase。這是uvm在設定default_sequence時的要求。由于除了main_phase外,還存在其他任務phase,如configure_phase、reset_phase等,是以必須指定是哪個phase,進而使sequencer知道在哪個phase啟動這個sequence。
至于set的第三個和第四個參數,以及uvm_config_db#(uvm_object_wrapper)中為什麼是uvm_object_wrapper而不是uvm_sequence或者其他,則純粹是由于uvm的規定,使用者在使用時照做即可。
其實,除了在my_env的build_phase中設定default_sequence外,還可以在其他地方設定,比如top_tb:
代碼清單 2-70
進而,objection完全與sequence關聯在了一起,在其他任何地方都不必再設定objection。