訪問者模式
Visitor Pattern:允許一個或者多個操作應用到一組對象上,解耦操作和對象本身。換言之,如果component的數據結構是比較穩定的,但其是易于變化的,那么使用訪問者模式是個不錯的選擇。
常見的訪問者模式有五種角色:
(1) Vistor(抽象訪問者):為該對象結構中具體元素角色聲明一個訪問操作接口。
(2) ConcreteVisitor(具體訪問者):每個具體訪問者都實現了Vistor中定義的操作。
(3) Element(抽象元素):定義了一個accept操作,以Visitor作為參數。
(4) ConcreteElement(具體元素):實現了Element中的accept()方法,調用Vistor的訪問方法以便完成對一個元素的操作。
(5) ObjectStructure(對象結構):可以是組合模式,也可以是集合;能夠枚舉它包含的元素;提供一個接口,允許Vistor訪問它的元素。
舉例:老師教學反饋得分大于等于85分或者學生成績大于等于90分,則可以獲得優秀獎;如果老師論文數目大于等于2、學生論文數目大于等于1,則可以獲得1萬元的現金。在這個例子中,老師和學生就是Element,他們的數據結構穩定不變。從上面的描述中,我們發現,對數據結構的操作是多變的,一會兒評選成績,一會兒評選科研,這樣就適合使用訪問者模式來分離數據結構和操作。
兩種操作,class GradeSelection class ResearchSelection 重寫實現 visit()函數
class Teacher class Studnet中調用 visitor.visit(),不需要特別指明哪一種操作。
訪問者模式適用于對一組結構固定的組件統一地加入一個新的操作,如上例,將student1,student2,teacher1,teacher2這四個共同屬性(都繼承于Element)的組件,加入ObjectStructure中,此例中的組件關系是前后線性的關系,依次加入element_q隊列中。當需要對所有組件執行researchselection操作時,ObjectStructure調用accept(researchselection)即可,會自動遍歷各個組件執行這個researchselection的操作。如果需要切換其他操作,只需更改ObjectStructure中accept的參數即可。添加新的操作也很方便,不需要更改組件,只需要在外部重寫visitor()函數,具有很好的擴展性,實現組件與操作的解耦。
UVM_PHASE
UVM的環境具有明確的結構,各個組件統一繼承于uvm_component, 組成 ** tree structure** 。每個子類component重寫各個phase函數,每個component中的phase函數相當于visitor(),很符合使用訪問者模式的條件。但是uvm的phase機制實現和上述介紹的示例還有很大區別,component中的phase是在自身內部實現的,而不是放在類外部;對于執行同一個phase,樹形結構中的component不是簡單的依次執行,有top-down,down-top和并行執行;對于同一個componet中的phase, 有不消耗時間的function phase, 也有消耗時間的task phase, 有依次執行的,也有并列執行的(run_phase和12個run-time phase)。所以存在兩個維度,一個uvm_component的維度,根據單例模式中的parent-child關系構建了樹狀結構;一個phase維度,將每個phase以node的形式放入domain中,統一調度。UVM還支持objection機制,drain_time,timeout,多domain,進程同步,phase的jump,phase_debug等操作,所以簡單的訪問者模式無法滿足要求。
下面根據源碼對uvm_phase從這兩個維度分析:
1. phase的執行順序
在第二篇,提到了uvm_root中函數run_test()根據工廠模式創建testcase的實例。接下來run_test()調用uvm_phase中的靜態函數m_run_phase()開始執行各個phase。這里用到了SV內建的class process,提供了精細控制進程的方法。
uvm_domain調用靜態函數get_common_domain(), 從名字上可以看到這是獲得一個domain。uvm_domain繼承于uvm_phase,是phase的一個容器。
comon_domain調用add函數依次加入不消耗時間的function phase, 一共9個。第一個uvm_build_phase::get()獲得build_phase的實例,uvm_build_phase和uvm_root一樣采用了單例模式,所以全局只有一個build_phase,通過 uvm_build_phaes::get()獲得。所以我們平常在不同component中的傳入的phase,其實指向的都是那個唯一的uvm_build_phaes::get() 實例。其他phase也一樣,實例全局唯一。
add()函數實現了將不同phase以node的形式加入,node有predecessor,successor的概念,構成鏈表結構。類似component中parent,child的關系,構成樹狀結構。根據add加入的順序,組織了先后順序。所以connect_phase_node的predecessor是build_phase_node, successor是end_of_elaboration_phase.
get_uvm_domain()獲得另一個domain:uvm_domain。uvm_domain中加入的是task phase, 一共12個。然后common_domain再將uvm_domain和run_phase同時加入,實現rum_phase和12個小phase組成的run-time phase并行執行。
到此,各個phase的執行順序就固定了。如果沒有單獨創建domain,那么只有common_domain和uvm_domain這兩個domain會被默認創建。結構如下:
回到m_run_phases(), 有一個forever begin ... end一直循環從m_phae_hopper.get()獲得phase執行exectute_phase()。最開始m_phase_hopper( 一個放入uvm_phase類型的mailbox)放入的是common domain,common domain執行完畢后,會將successor build phase 放入m_phase_hopper中;在forever循環中m_phae_hopper.get()獲得build phase,執行完build phase最后會放入build phase的successor connect phase,直到遍歷完所有phase。
2. component的執行順序
接上面,phase.execute_phase()的具體實現會根據不同的uvm_phase_type和uvm_phase_state走不同的分支。
uvm_build_phase, uvm_final_phase繼承于uvm_topdown_phase, 其余function phase繼承于uvm_downtop_phase, task phase繼承于uvm_task_phase。
對于build_phase, 函數exectue_phase會調用m_imp.traverse(top,this,UVM_PHASE_EXECUTING), traverse()函數在uvm_topdown_phase中定義,build_phase從top to down的執行順序也是在這里實現的。
最開始traverse傳入的是top,也就是最頂層uvm_root,②處調用get_first_child獲得uvm_test_top(在之前的run_test中已被創建),遞歸調用traverse函數,執行ph.execute(uvm_test_top, phase), 實際調用的是comp.build_phase, 也就是uvm_test_top的 builde_phase()。builde_phase會創建uvm_test_top的child env。執行完ph.exectute,再次執行traverse函數,此時傳入的component是env, 執行env的build_phase,創建env的child agt。一直遞歸循環,實現所有component的創建。
對于繼承uvm_downtop_phase的phase,則是從底部開始循環。相比uvm_topdowun_phase,將遞歸函數traverse放在ph.execute前面,便實現了down to top的順序。
對于繼承uvm_task_phase的函數,雖然遞歸函數traverse放在ph.execut前面,也是down to top的順序,但是fork ... join_none讓不同component的同一種phase函數在不同process上同時執行,實際效果是一塊同時運行的。
所以對于component中phase的執行遍歷,是根據調用遞歸函數遍歷child完成的。在uvm_root中的find()函數,也是遞歸調用函數完成遍歷。
uvm objection
uvm objection涉及對進程的控制,先介紹一下systemverilog提供的class process。(計算機體系中的進程,線程和內核的調度有關,而仿真平臺是跑在仿真軟件上的,由 simulation kernel進行調度 (IEEE 4.Scheduling semantics clause) 按照時間片執行,以下進程,線程不做區分,統一叫做進程)
SV中的fork相關函數可以創建新的進程,但是對于進程的管理, 只有 wait fork, disable identifier, disable fork這些。其他語言中一般都有專門管理進程的操作方法,比如python中的multiprocessing模塊。所以SV中加入了一個class process,提供了更精細的進程管理。class process并不可以用于創建進程,只可以在initial begin ..end,always,fork等創建進程的語句中通過process::self()獲取該進程;status()獲得進程狀態,kill()終止進程及子進程,suspend()掛起進程,resume()恢復進程,srandom(int seed)設置進程的隨機種子。
uvm objection機制的源代碼實現不再探究,總結需要注意的幾點:
每個phase的實例是唯一的,每個phase在創建的時候,自動創建了屬于這個phase的objection。
對于消耗時間的task phase,其中必須raise_objection, 才會執行,否者直接退出。
對于同一個phase,可以多次raise_objection, 但是raise_objection和drop_objection必須成對存在。只有raise數量等于dorp數量時才會退出這個phase。
raise_objection前面不可以有消耗時間的語句,也就是剛進入phase的0時刻,就需要檢測到raise_objection, 否則直接退出這個phase。
對于run_phase和并行的12個task phase, 如果在run_phase中raise_objection,但是main_phase沒有raise_obejection,那么main_phase直接退出。如果在main_phase有raise_obejection,run_phase沒有raise_objection,run_phase也會執行。所以盡量run_phase和12個小phase不要同時使用,以免出錯。
通過簡單的代碼使用process來實現一下UVM中的objection:
do_monitor是一個無線循環,在driver_main_phase中控制objection的raise和drop。
如果line42加上時間延遲,則會直接退出main_phase,進入下一個phase. 如果注釋掉line43行也是直接退出main_phase,進入下一個phase. 打印結果:
如果加上line49, line51,main_phase則無法退出。打印結果:
uvm_visitor
UVM1.2中新加入了訪問者模式的基礎類,供使用者擴展使用:
uvm_visitor:提供了visit()方法,以及begin_v(),end_v()兩個hook。
重寫visit方法,簡單的打印component的full_name.
uvm_visitor_adapter:提供了accept函數,用于實現visitor和component的連接,并對每個component調用visti方法。
env中創建visitor, adapter的實例,accept傳入的是env這個comonent,打印處uvm_test_top.env
對于component的組織調用順序,用戶可以自定義structure。UVM提供了三種sturcture, 對應的adapter,如下:
使用的top to down的structure, 從上到下遍歷component調用visit()函數:
審核編輯:湯梓紅
-
UVM
+關注
關注
0文章
182瀏覽量
19189 -
數據結構
+關注
關注
3文章
573瀏覽量
40154 -
設計模式
+關注
關注
0文章
53瀏覽量
8645
原文標題:UVM設計模式 (六)訪問者模式、uvm_phase、uvm objection、process control
文章出處:【微信號:數字芯片設計工程師,微信公眾號:數字芯片設計工程師】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論