sequence其實不屬於驗證平台的一部分,他是object而不是component,但是sequence跟component的sequencer密切相關。只有在sequencer的幫助下,sequence產生出的transaction才能最終送給driver。這里就有幾個問題:如何去理解transaction? sequencer是如何把sequence送給driver的?transaction可以理解為一個具備某種數據結構的一個數據,一個transaction里面有一包或者一幀數據的很多特征信息,sequence就是一個裝滿了transaction,可以有各種各樣的transaction。如果把sequence比作彈夾,transaction比作子彈,那么sequencer就是一把槍,一把槍可以有很多彈夾吧,一把槍只有有彈夾,彈夾有子彈,那么這把槍才有意義吧?借這個比喻,如果一個子彈(transaction)打出去了,那么該子彈的聲明周期到期了,但是彈夾(sequence)沒有,因為里面還有很多子彈,等子彈打完了,sequence的生命周期也就到期了。
sequence派生自uvm_sequence,transaction派生自uvm_sequence_item,在定義彈夾(sequence)的時候要指定用什么子彈(transaction),當一個sequence驅動之后,會自動執行sequence里面的一個body task,將一發發子彈打出去。有兩種方式打子彈:1.使用uvm_do這個宏;2.使用start_item和finish_item任務完成。解釋:使用uvm_do這個宏,方便但是靈活性比較差,因為uvm_do宏封裝很多操作,封裝的越多,靈活性越差。uvm_do做了三件事,第一件,創建一個自定義的transaction的實例,第二,將其隨機化,第三將其發送給sequencer。如果不是用uvm_do這個宏,如何完成上述工作?可以使用start_item和finish_item這兩個任務。這連個任務做了啥?其實這兩個任務分別封裝了跟加底層,更加靈活的函數和任務,但是一般使用start_item和finish_item就足夠了。
現在明白了sequence這個彈夾要把transaction這些子彈通過手槍sequencer打出去,那么彈夾(sequence)把子彈(transaction)送給手槍(sequencer),讓手槍(sequencer)發射有沒有一個握手或者請求通知之類的呢?一個sequence在向sequencer發送transaction前,先向sequencer發送一個請求,sequencer把這個請求放在一個仲裁隊列中。這樣,sequencer就只要做兩件事,一是檢查有沒有sequence要發送transaction的請求,二是driver有沒有申請transaction(別人沒請求你開槍,就不能亂開槍吧?一旦請求了開槍,總得要看看槍里有沒有准備好的子彈吧?)。
那么問題來了,driver是如何向sequencer申請transaction的呢?在uvm_driver中有成員變量seq_item_port,而在uvm_sequencer中有成員變量seq_item_export,這兩個port端口在agent的connect_phase連接到一起了(手動去連接),driver是主,sequencer是從。當二者連接好了之后,driver調用get_item_item這個任務,任務的參數就是transaction類型的,這樣就向sequencer申請了新的transaction。由此可見,driver只是負責獲取並且驅動transaction而不負責去產生transaction。驅動transaction部分自己按照時序要求去寫,驅動走了一個transaction后,要不要通知sequencer,你給我的transaction我已經成功發送出去了?答案是要的,這是一種增加可靠性的握手機制。那么driver如何告訴sequencer我的transaction發送OK了?驅動完成后調用item_done通知sequencer,在沒有通知之前,sequencer其實自己會保留一份剛剛送給driver的transaction,萬一driver沒有收到,那sequencer就再發一次。當sequencer收到item_done之后,sequencer就認為driver已經得到了這個transaction,將會把這個transaction刪除,此時,sequence中的uvm_do宏才算是執行完畢。
driver向sequencer那里拿transaction,sequencer的transaction又是從sequence中來的,那么問題來了,sequence是如何把transaction給sequencer的呢?只需要在某個component中去啟動這個sequence,在啟動sequence的時候,要指定是哪個sequencer。其過程是首先創建一個自定義的sequence的實例seq,之后調用start任務,穿三就是啟動這個transaction的sequencer指針。這個過程是在run_phase(main_phase)中完成的。注意在調用start任務前后,要調用phase.raise_objection和phase.drop_objection。在很多main_phase中,objection會伴隨着sequence,sequence這個彈夾把里面的子彈transaction打完了,仿真就可以結束了,也就drop_objection了。
還有一種default_sequence,用法稍微簡單點,在某個component例如env中使用uvm_config_db去指定,哪個sequencer的哪個phase把哪個sequence指定為default的,這種自動的方式啟動sequence用的地方還是比較多的。
今天就總結寫入門的東西,理清之間的關系,后面sequence的一些高級用法,再總結。