天天看點

UVM知識點總結-UVM中的通信機制

UVM中的通信

  1. TLM0
    1. 概述:TLM,事務級模組化,transaction level是相對于DUT中各個子產品之間的信号級别的通信來說的。  僅僅元件可以例化端口,transaction不能例化端口,因為transaction是繼承與object.(1)put/get/peek;transport/master/slave(前三種是方法,還對應着try和can)(2)port/export/import  (3)blocking/nonblocking
      1. port之間的連接配接:(1)這三種端口按照控制流優先級排列,其中port優先級别最高,import最低(2)port可以連接配接自己和另外兩個對象;export隻可以連接配接自己和import,;export隻能作為事務的傳輸者,不能作為事務的終點;import隻能被連接配接,也就是他隻能作為發生事務的終點(3)一個port可以連接配接到多個port上,但是一個port不能連接配接到多個import上,一個port能否連接配接到多個export上??一個export能否連接配接到多個inport上??(4)port之間的的連接配接同樣支援階層化的嵌套
      2. 方法的調用:(1)peek和get的差別,對于FIFO來說,get任務被調用時FIFO内部的緩存中會少一個transaction,但是對于peek來說,調用時會把FIFO會把transaction複制一份發送出去,其内部緩存的transaction并不會減少
      3. 阻塞與非阻塞:(1)如果沒有寫blocking或者nonblocking,那麼實際上對阻塞和非阻塞均支援
    2. 單向通信
      1. 建立傳輸資料的類型,定義兩個事務類型
      2. (1)建立端口,兩個傳輸的元件之間的端口要成對,阻塞與否、傳遞的資料類型要一緻(2)uvm_blocking/nonblocking_put/get/peek(根據阻塞與否加try和can)_port/export/import #(xx,xx) 執行個體名;(3)注意(xx,xx)是根據是initiator還是target來決定的,對于initiator來說,括号内僅僅一個發送的對象即可,但是對于target來說括号内不僅僅要有發送的對象(位于第一個參數),還要有target所在類的類名(3)對于target來說,多傳遞了一個類名的根本原因要想完成資料的發送一方面需要initiator連接配接了target,另一方面initiator調用target中的方法,那麼initiator如何調用target一側的方法呢?實際上就是通過這裡傳遞了target所在類的類名實作
      3. 頂層進行連接配接,連接配接的方向是從initiator(左側)的port一端連接配接到了target的import(右側)的一端,比如c1.bp_port.connect(c2.bp_imp),bp_port和bp_imp是執行個體名
    3. 雙向通信
      1. transport:transport對應到建立端口的語句中與put/get/peek是相同的位置,調用這個transport表示在一次傳輸過程中既可以完成req的傳輸,也可以完成rsp的傳回,initiaor都是A,target均是B,先Aput到B,再從Bget回A(
      2. master/slave:(1)兩者的差別在于initiator作為master時,是先将req放到target中再從中拿回rsp;而initiator作為slave時,是先從target中擷取req再将rsp放回target(2)master/slave必須通過put/get/peek調用,使用兩次才能完成一次握手通信
      3. #(xx,xx,xx)變成2(initiator)或3(target)個參數,依次是發出去的,傳回來,target所在類名(前兩個是針對initiator的發和返)
    4. 多向通信(解決具有多個相同類型的端口但是調用的方法無法重名的問題)
      1. (1)通過宏定義兩個相同的類型但不同名稱的方法,`uvm_blocking/nonblocking_put/get/peek(根據阻塞與否加try和can)_port/export/import_decl (字尾名);具體的方法實際上是在target所在類中實作,是以initiator所在元件基本上不做改變,其建立端口的語句中方法部分也沒有加上字尾,但是在target一側,其建立端口的語句及具體定義的方法都需要加上字尾(2)對于initiator一側,調用的端口類型即使相同也不需要區分
    5. 通信管道
      1. Analysis Port

(1)uvm_analysis_port,一端到多端,initiator到多個target,port到inport連接配接,允許沒有連接配接任何一個target(也就是inport)(2)這個過程不同與端到端的連接配接,這種觀察者模式(廣播模式)既可以連接配接一個對象,也可以連接配接多個對象,同樣不連接配接對象,而前面的端與端之間的連接配接必須要求兩端的連接配接,本身是一種的過程,不存在阻塞的概念(3)這個過程不同于之前的put,他調用的是write函數,不耗時間,是以analysis port實際上也是不耗時的,也僅能調用write函數,在target所在類中需要定義write函數(4)analysis_port解決的是一個port與多個import連接配接,實際上export和import也可以像這樣連接配接,也就是一個export與多個import連接配接,隻需将analysis_port替換成analysis_export即可(5)analysis_port可以和analysis_export相連接配接,但是前提是analysis_export後面還有個analysis_import,直接連接配接後面不加analysis_import會報錯

monitor與scoreboard連接配接中的一些問題:(1)頂層monitor與scoreboard之間的連接配接:一共有三種辦法解決,分别是o_agt.mon.ap.connect(scb.scb_imp),此種方法階層化關系不太好;在agent中build_phase聲明一個ap并且執行個體化,ap=new("ap",this),并且在agent類中的connect phase中将這個聲明的ap與monitor的ap相連接配接,mon.ap.connect(this.ap),随後在頂層中直接将scoreboard的scb_imp與agent的ap相連接配接,o_agt.ap.connect(scb.scb_imp),這種方法可能看起來較為麻煩;第三種是在agent的connect_phase中聲明一共ap但是不執行個體化他,讓其指向monitor中的ap,ap=ap.monitor;上述方式的後兩者的前提是在agent中都通過uvm_analysis_port定義了端口ap  (2)scoreboard不僅要和monitor連接配接,同時需要和reference modle連接配接,但是scoreboard的write函數僅僅有一個:類似與多向通信的操作,針對scoreboard通過宏定義字尾,比如`uvm_analysis_imp_decl(_monitor)和`uvm_analysis_imp_decl(_model),相應的定義write方法時也需要加上字尾

      1. FIFO

TLM FIFO,(1)uvm_tlm_fifo繼承與uvm_component,uvm_tlm_fifo與uvm_tlm_analysis_fifo 均是FIFO,後者繼承與前者,兩者的差別在于後者有analysis_export的端口并且有一個write函數,很多情況下兩者可以同時适用(2)FIFO調試的一些函數:is_empty函數用于判斷目前的FIFO緩存是否為空,is_full判斷是都為滿,flush用于清空緩存中的所有資料,一般用于複位操作,原型是virtual function void flush();設定FIFO餘量的函數,function new(string name,uvm_component parent =null,int size =1),fifo本質上是一個component,是以前兩個參數和component一緻,第三個參數表示FIFO餘量,如果填0的化表示餘量無窮大

Analysis TLM FIFO

繼承于uvm_tlm_fifo,uvm_tlm_analysis_fifo  其作用在于一方面起到了uvm_tlm_fifo緩存的作用,另一方面添加了uvm_analysis_port中的port,inport端口及write函數

應用:通過Analysis TLM FIFO實作scoreboard主動接收資料(前面的Analysis Port隻能實作monitor主動發送資料,scoreboard被動的接收資料),這裡的uvm_tlm_analysis_fifo的本質是一塊緩存加兩個import。具體的操作方式是在agent和scoreboard之間增加一個uvm_tlm_analysis_fifo。在monitor與與uvm_tlm_analysis_fifo之間連接配接時,monitor與uvm_tlm_analysis_fifo的端口分别時port和import,而在scoreboard與uvm_tlm_analysis_fifo的連接配接中,兩個端口分别時blocking_get_port和import(也就是uvm_tlm_analysis_fifo的兩個端口均是import)

上述應用的具體例子是兩個agent(或monitor)與scoreboard之間的連接配接,具體的方法展現在connect上(也就是定義好端口類型後實際上不需要在單獨定義中間的FIFO,隻需要在頂層連接配接時進行操作即可)。經過mdl後,scoreboard改變了原來的隻能被動的從agent中monitor中接收資料,也可以主動的拿資料,也就是主動的從mdl_scb_fifo中blocking_get,然後mdl_scb_fifo間接的從mdl中拿資料。白皮書p125

      1. analysis port(往往代表着import)與FIFO之間的對比:(1)對于使用端口數組的情況下,采用FIFO 進行傳遞優于import,原因在于采用import進行連接配接,其例化的端口往往無法使用for循環語句,但是在FIFO中可以使用
      2. Request & Response
  • 具體的分類包括:(1)uvm_tlm_req_rsp_channel類,内部例化了兩個信箱,分别是protect uvm_tlm_fifo #(req) m_request_fifo,protect uvm_tlm_fifo #(rsp) m_response_fifo,兩個端口,分别調用兩次方法;(2)master_port/slave_port ,前者在連接配接操作上的另一種形式,雖然是一個端口,但是任然需要調用兩次方法;(3)uvm_tlm_transport_channel,在前面兩者的基礎上的另一種,是uvm_tlm_req_rsp_channel的子類,與master_port/slave_port兩者差別在于uvm_tlm_transport_channel在initiator一側通過transport改變了原有的req/rsp形式,是以initiator一側隻要調用一次transport就可以實作req和rsp的傳輸,而靠近 target一側任然是一個端口調用兩次方法的形式(slave端口),相較于uvm_tlm_req_rsp_channel新例化了transport端口,uvm_transport_imp #(req,rsp,this_type) transport_export.
  • 三種通信連接配接的具體形式:(1)兩個端口,每個端口調用兩次方法,initiator.put_port.connect(req_rsp_channel.put_request_export);target.get_peek_port.connect(req_rsp_channel.get_peek_request_export);target.put_port.connect(req_rsp_channel.put_response_export);initiator.get_peek_port.connect(req_rsp_channel.get_peek_response_export)  (2)initiator.master_port.connect(req_rsp_channel.master_export),target.slave_port.connect(req_rsp_channel.slave_export),上面這兩種方式實際上都是uvm_tlm_req_rsp_channel,隻是說有兩種不同的連接配接過程(3)第三種方式僅僅在initiator端進行了改變,在target一側可以使用第一種或者第二種方式連接配接,initiator.transport_port.connect(transport_channel.transport_export),
  • 總的來說,需要注意以下幾點:(1)相較于雙向管道來說,相當于不需要自定義函數複制(2)是面向雙向通信的fifo,之前的一系列均是面向單向通信管道(3)master_port/slave_port與uvm_tlm_transport_channel的差別在于:對于後者initiator放入req後不會結束(第一步),随後target進行get到req(第二步),随後targetput入rsp(第三步),最後initiator進行get到rsp(第四步),在這裡進行完第一步後transport并沒有結束,而是必須等到transport拿回rsp(也就是第四步)結束後transport才會結束,也就是第一步和第四步必須是同步結束的,而對于第二部和第三步由于target一側實際上沒有采用transport進行傳輸,任然是slave,是以第二步和第三步其結束過程是獨立的。而master_port/slave_port形式的端口由于initiator一側也沒有使用transport,是以他的四個過程(兩個put操作兩個get操作)均是獨立結束的,執行完就結束
  1. TLM0(解決UVM和system c之間的連接配接問題)
    1. 接口實作:(1)端口類型:uvm_tlm_b/nb_***_initiator/target_stocke,b和nb分别代表阻塞和非阻塞,blocking和noblocking兩種transport方式,.blocking的傳輸方式要求在一次傳輸過程中,完成request和response的傳輸;nonblocking的傳輸方式則将request和response的傳輸分為了兩個獨立的單向傳輸,而兩次傳輸整體視為完成一次握手傳輸。(2)傳輸方法:參見講義
    2. 傳送資料(定義标準的資料包)
      1. 資料類型統一是uvm_tlm_generic_payload類,為了标準化,不建議在此類的基礎上進行擴充,具體的标準化内容參見講義,比如 : uvm_tlm_b_initiator_stocket  xx,注意這裡的initiator沒有指定具體的資料類型,這是因為資料類型已經固定,确定就是uvm_tlm_generic_payload類型,stocket也是标準化的,是各種port的組合
      2. 此外,在面臨現有的資料類型無法通過标準化的資料進行表示時,有兩種方法可以解決:(1)将其合并作為資料成員data數組的一部分,傳到給target一側時會自動解析(2)uvm_tlm_generic_payload::set_extension(uvm_tlm_extension_base xx)将這個資料類型set進去,uvm_tlm_extension_base是标準資料類型中針對标準無法覆寫時提供的擴充選項
    3. 時間标記,往往在systemc中自建時鐘會導緻運作效率的降低,uvm_tlm_time,uvm中建立的時間類,起一個時間标準的作用
  2. 通信原件的同步
    1. uvm_event
      1. 一些概念:(1)資源池uvm_object_string_pool #(T);T表示的是uvm_object,pool表示的是一個資源池,資源是按字元串索引的關聯數組的形式存放的,而對于uvm_event_pool來說,實際上隻是将其中的T變為uvm_event,uvm_event_pool是其的子類(2)對事件的建立可以調用uvm_event_pool,比如e1=uvm_event_pool::get_global("e1"),其原則是如果前面有uvm_event e1,則會建立e1;如果沒有,那麼會自動建立一個e1
      2. 基本的點:(1)wait_ptrigger相當于電平觸發的意思(等價于SV中的trigger),而wait_trigger相當于SV中的@(必須在同一時刻時,該事件沒有發送過,如果之前發生過,那麼實際上等不到)(2)e1.trigger()括号中可以加内容也可以不加,加了比如wait_trigger(d)(d表示具體的資料),那麼此時需要用wait_trigger_data語句等,對應到前面的資料傳輸,也就是等到前面的資料到了,如果前面沒有d,那麼用wait_trigger()也可以。另外,wait_trigger_data(xx)中的xx預設是object類型,如果後續需要通路子類的對象,需要使用類型轉換函數$cast(3)uvm_event觸發事件,wait_trigger/ptrigger等待事件,事件觸發後,uvm_event需要通過reset()重制狀态,随後才可以繼續觸發
      3. 相關函數:(1)回調函數,e1.add_calback(d),e1是事件,d是回調函數,其定義要在與uvm_event_callback的子類進行定義,通過這種辦法将事件e1和回調函數d綁定在一起,此外在uvm_event_callback的子類中還可以定義wait_trigger調用前後的pre_trigger和post_trigger,pre_trigger需要有傳回值,傳回值為1時不執行後續的trigger和post_trigger,傳回值為0繼續執行後續任務(2)uvm_event可以通過get_num_waiters()擷取等待他的程序數1
      4. object和component之間發生同步是無法通過端口的進行同步的,因為object無法例化端口,是以這兩者可以通過event進行觸發并且也可以發送一些資料;另外object與object之間也可以借助uvm_event完成一些資料的通訊,比如sequence與sequence之間進行同步,或者sequence與driver之間進行同步
    2. uvm_barrier:uvm_barrier b1;b1.wait_for();b1.set_threshold(xx)
    3. uvm_callback
      1. (1)基本步驟:預留回調函數入口,定義回調函數,在頂層例化回調函數及添加回調類的執行個體,(2)例子:定義回調函數 class cb1 extends uvm_callback; `uvm_object_untils(cb1)  這一步時對回調函數進行注冊,uvm_callback屬于uvm_object,後續在該類中定義具體的任務或函數,比如do_trans******将元件與回調函數關聯,并将回調函數插入元件中,這兩個步驟都需要在具體的元件類中(比如類comp1,該類繼承與uvm_component),關聯 `uvm_register_cb(comp1,cb1),插入元件`uvm_do_callbacks(comp1,cb1,do_trans(d))*******添加回調函數執行個體uvm_callbacks #(comp1)::add(c1,m_cb1),c1是comp1的執行個體,m_cb1是回調函數類cb1的執行個體 
      2. (3)回調函數間的繼承需要注意的問題:在上面的基礎上,假設有另一個類cb2繼承與cb1,也就是有兩個回調函數,但是假如在元件comp1中進行操作時,隻對cb1進行了關聯和插入,在添加回調函數執行個體的操作時對兩個回調類均添加了執行個體,在這情況下cb2回調函數同樣是可以添加進來的,原有在于cb2繼承與cb1,也就是一旦發生繼承關系時,就不需要在元件comp1中對子類的回調函數進行綁定和插入,隻需要在在元件中對父類進行綁定和插入,那麼在随後添加時就會自動執行子類的回調函數
      3. (4)子類父類回調函數同名時,按照上面的情況,如果cb1和cb2中均有一個同名的任務do_trans,在添加元件是,對父類的回調函數cb1不添加,而隻添加子類的回調函數cb2,那麼cb2會覆寫cb1的do_trans,是以通過這種頂層改變添加的回調函數的方式可以對類進行很好的修改
      4. (5)回調函數的退出:多個回調函數同時存在時,預設情況下時按照順序進行,但是也可以通過`uvm_do_callbacks_exit_on的方式控制回調函數的退出,這個宏一共有四個參數,分别是元件類,回調函數類,回調函數中具體方法,值a,回調函數會一直執行,直到傳回值a時退出

繼續閱讀