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