UVM(七)之phase及objection
UVM(七)之phase及objection
這兩個概念與UVM驗證平臺息息相關,phase就好比鐵軌,讓UVM這趟列車在鐵軌上向前執行,不會脫軌,不會跳過某一段而直接到達後一段,objection則更像是能量,給列車提供能量,控制著這趟列車何時終止。
phase
1.為什麼要分成phase
verilog中有非阻塞賦值和阻塞複製,相應的,在模擬器中要實現NBA區域和Active區域,這樣在不同的區域做不同的事情,可以避免競爭關係的存在導致的變數值不確定的情況。同樣的,一個驗證平臺是很複雜的,要搭建一個驗證平臺是一件相當複雜的事情,要正確的掌握這些步驟並理順他們是相當難得一個過程。
舉一個簡單的例子,一個env下面會例項化agent,scoreboard,reference model等,agent下面又會有sequencer,driver,monitor。而且這些元件之間還有連線關係,如agent中monitor的輸出要送給scoreboard或reference model,這種通訊的前提要先把兩者連線起來,reference model要和scoreboard連線在一起,可以如下寫:
scoreboard = new; reference_model = new; reference_model.connect(scoreboard); agent = new; agent.driver = new; agent.monitor = new; agent.monitor.connect(scoreboard);
這裡反應出來的問題就是最後一句話一定要放在最後寫,因為連線的前提是所有的元件已經例項化了。但是相應的, reference_model.connect(scoreboard)要求就沒有那麼高,只需要在上面程式碼中reference_model = new之後的任何一個地方寫就可以了。
UVM採用的方式:例化放在build_phase來做,而連線關係放在了connection_phase來做,不同時間做不同的事情,這就是UVM中phase的設計哲學,UVM中常用的phase如下:
2.UVM中同一phase的執行順序
上面說明了不同的phase之間的執行順序,由於phase是和uvm_component相伴相生的一個概念,對於每一個uvm_component來說,都有上面說得phase。而驗證平臺中的component又是分層次的。如agent下面有driver和monitor,那麼同樣是build_phase,先執行agent的build_phase還是先執行driver的build_phase或者是monitor的build_phase?
對於這個問題,如果撇開UVM,那麼有多種答案,如遵循自上而下的順序,先執行agent的,再執行driver和monitor的,也可以遵循自下向上的順序,先執行driver和monitor的,再執行agent的或者完全採用一種亂序的方式,按照隨意的順序執行。最後一種方式是不受人控制的,在程式設計的過程中,這種不受控制的程式碼越少越好,因此可以選擇的無非就是自上向下或者自下向上。UVM採用了自上向下的執行順序,這種鑽則其實是唯一的。
假設UVM不使用自上而下的方式執行build_phase,那麼會是怎麼個情況呢?UVM的設計哲學就是在build_phase中例項化的工作,drive和monitor都是agent的成員變數,所以他們的例項化都要在agent的build_phase中執行。如果在agent的build_phase之前執行driver的build_phase,此時driver還根本沒有例項化,所以呼叫driver.build_phase只會引發錯誤。
到了這裡,有必要澄清一個概念,那就是UVM是在build_phase做例項化工作。這裡的例項化指的是uvm_component及其派生類變數的例項化,加入在其他phase例項化一個uvm_component的話,那麼系統就會報錯的。如果是uvm_object的例項化,那麼可以在任何phase完成,當然包括build_phase了。
除了自上而下的執行方式外,UVM的phase還有一種執行方式是自下而上。事實上,除了build_phase之外,所有的不消耗模擬時間的phase(即function phase)都是自下而上執行的。如對於connect_phase,限制性driver和monitor的connect_phase,再執行agent的connect_phase。
本節說得都是不消耗模擬時間的phase,即function phase的執行情況,那麼對於run_phase等的執行,他們是如何執行的呢?事實上,他們也是按照自下而上的順序執行,與前面的function phase自下而上執行不同的是,這種task phase由於是耗費時間的,所以它並不是等到下面的phase執行完了才執行上面的phase的,而是把這些run_phase通過fork.......join_none的形式全部啟動起來。所以,自下而上的執行,其更準確的說法是自下而上的啟動,同時在執行。
3.UVM中動態執行(run_time)phase
UVM把run_phase又分割成了12個phase,這12個小的phase各自在執行順序方面與run_phase完全相同,即自下而上的啟動,同時執行。這裡有兩個問題,第一個問題就是為什麼要分成小的phase?第二個問題就是這12個小的phase與run_phase之間關係如何?
分成小的phase是為了精細化控制。如這12個小phase的名字所示,reset,configure,main,shutdown四個phase是核心,這四個phase通常也是模擬了DUT的正常工作方式,在reset_phase對DUT進行復位,初始化等操作,在configure_phase則進行DUT配置,DUT的執行主要在main_phase完成,shutdown_phase則是做一些與DUT斷電相關的操作。通過細分,對DUT實現更加精確的控制。如,假設要對DUT在執行過程中要進行一次reset操作,在沒有細分phase之前,這種才做要在scoreboard,reference model等加入一些額外的程式碼來保證驗證平臺不會出錯。但是有了這些小的phase之後,分別在scoreboard和reference model及其它部分(如driver,monitor等)的reset_phase寫好相關程式碼,之後想做一次復位操作,那麼只要通過phase的jump,就會自動的條狀回reset_phase。
第二個問題的:這12個動態執行的phase與run_phase之間是並列的關係,其執行結構大概如下所示:
這段程式碼只是形象的說明這12個小的phase與run_phase之間的關係,但是有一點要指出的是,這12個小的phase之間並不是這樣順序執行,而是每當一個小的phase執行完後,才會進入下一個小的phase,也就是說有一個同步過程。這段程式碼中並沒有體現出這種同步過程。
objection
1.UVM中的objection
在沒有uvm的時候,我們寫testbench時,要自己決定什麼時候把testbench關掉,通常會呼叫$finish函式。在UVM中,通過objection機制來控制驗證平臺的關閉。
4.1obejection控制驗證平臺的關閉
objection子main的意思就是反對,異議。在驗證平臺中,可以通過放棄異議(drop_obejection)來通知系統可以關閉驗證平臺。當然了,在放棄之前,首先要提起異議raise_objection:
在進入到某一phase的時候,UVM會手機此phase提出的所有的objection,並且實時監測所有的objection是否已經drop了,當發現所有的都已經drop後,那麼就會關閉此phase,開始進入下一個phase。當所有的phase都執行完畢後,就會呼叫$finish來把整個驗證平臺關掉。
4.2引數phase的含義
在UVM中所有的phase的自動執行函式(任務)的引數中,都有一個phase:
task main_phase(uvm_phase phase);
這個輸入引數中的phase是為了便於任何component的main_phase中都能raise_objection,而要raise_objection則必須通過phase.raise_objection來完成,所以必須把phase作為引數傳遞到main_phase等任務中。可以想象,如果沒有這個phase引數,那麼需要raise一個objection就會比較麻煩。
4.3一般在什麼地方raise_objection
在driver中raise_objection的時刻並不多,這是因為driver中通常都是一個無限迴圈的程式碼,如下所示:
如果是在while(1)的前面raise_objection,在while迴圈的end後面drop_objection,那麼由於無限迴圈的特性,phase.raise_objection永遠不會被執行到。
一種常見的思維是把raise_objection放在get_next_item之後,這樣的話,就可以避免無限迴圈的問題,確實如此。但是關鍵問題是如果其他地方沒有raise_objection的話,那麼如前面所言,UVM不等get_next_item執行完成就已經跳轉到了post_main_phase。
在monitor中,scoreboard中,reference model中都有類似的情況,他們都是無限迴圈的。要解決這個問題,一種方法就是不要讓driver無限迴圈,這個就需要知道在每次執行中需要發多少個item:
另一種方法就是在sequence中把sequencer的objection給raise起來,當sequence完成後,再drop此objection。這種方法相對上一中方法的好處就是不必設定要傳送的包的數量。不過,這種方法的限制是,此sequence必須要作為sequencer的某個phase(比如main_phase)的default_sequence,這個通常是比較容易的,一般都使用這種方法。