• 正文
    • Sequencer
    • Driver-Sequence API
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

【UVM COOKBOOK】Sequencer與Driver-Sequence API

03/26 07:25
283
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

Sequencer

sequence和它們的目標(biāo)driver之間的req和rsp item的傳輸是通過在sequencer中實(shí)現(xiàn)的雙向 TLM 通信機(jī)制來實(shí)現(xiàn)的。uvm_driver 類包含一個(gè) uvm_seq_item_pull_port,它和sequencer中的 uvm_seq_item_pull_export。port和export類是sequence_items 類型參數(shù)化的。

一旦建立了port和export連接,driver代碼就可以使用export中實(shí)現(xiàn)的 API 從sequence中獲取請求 sequence_items 并將rsp返回給它們。

上圖是連接關(guān)系,下面是對應(yīng)的代碼示例

//?Driver?parameterized?with?the?same?sequence_item?for?request?&?response?
//?response?defaults?to?request?
class?adpcm_driver?extends?uvm_driver?#(adpcm_seq_item);?
?....?
endclass:?adpcm_driver?
//?Agent?containing?a?driver?and?a?sequencer?-?uninteresting?bits?left?out?
class?adpcm_agent?extends?uvm_agent;?
?adpcm_driver?m_driver;?
?adpcm_agent_config?m_cfg;?
//?uvm_sequencer?parameterized?with?the?adpcm_seq_item?for?request?&?response?
?uvm_sequencer?#(adpcm_seq_item)?m_sequencer;?
//?Sequencer-Driver?connection:?
?function?void?connect_phase(uvm_phase?phase);?
????if(m_cfg.active?==?UVM_ACTIVE)?begin??//?The?agent?is?actively?driving?stimulus?
??????m_driver.seq_item_port.connect(m_sequencer.seq_item_export);??//?TLM?connection?
???m_driver.vif?=?cfg.vif;??//?Virtual?interface?assignment
??end?
?endfunction:?connect_phase?

driver和sequencer之間的連接是一對一的,不會(huì)出現(xiàn)多對一也不會(huì)出現(xiàn)一對多。除了標(biāo)準(zhǔn)端口,driver里還有一個(gè)analysis_port,可以連接sequencer中的一個(gè)analysis_export,實(shí)現(xiàn)driver和sequencer之間的單向響應(yīng)通信路徑。不過這是一個(gè)歷史遺留組件,不常用,要使用這個(gè)口的話,下面是示例代碼。

//?Same?agent?as?in?the?previous?bidirectional?example:?
class?adpcm_agent?extends?uvm_agent;?
?adpcm_driver?m_driver;?
?uvm_sequencer?#(adpcm_seq_item)?m_sequencer;?
?adpcm_agent_config?m_cfg;?
//?Connect?method:?
?function?void?connect_phase(uvm_phase?phase?);?
??if(m_cfg.active?==?UVM_ACTIVE)?begin?
??????m_driver.seq_item_port.connect(m_sequencer.seq_item_export);??//Always?need?this?
??????m_driver.rsp_port.connect(m_sequencer.rsp_export);??//?Response?analysis?port?connection?
???m_driver.vif?=?cfg.vif;?
??end?
??//...?
?endfunction:?connect_phase?
endclass:?adpcm_agent?

如果要在返回rsp的時(shí)候通知其他組件,可以用這個(gè)口,否則不要用。

Driver-Sequence API

uvm_driver 是 uvm_component 類的擴(kuò)展,它添加了一個(gè) uvm_seq_item_pull_port,來和sequence通信。uvm_driver 是一個(gè)參數(shù)化類,它被參數(shù)化為req sequence_item 的類型和rsp sequence_item 的類型。進(jìn)一步地,這些參數(shù)用于參數(shù)化 uvm_seq_item_pull_port。rsp sequence_item 可以獨(dú)立參數(shù)化的事實(shí)意味著driver可以從req類型返回不同的item類型。實(shí)際上,大多數(shù)driver對req和rsp使用相同的item,因此在源代碼中,rsp item類型默認(rèn)為req item類型。

uvm_driver 類的行為模型是它使用握手通信機(jī)制從sequencer req FIFO 中獲取sequence_items,并將rsp sequence_items 返回到sequencer rsp FIFO(也可以不返回)。uvm_driver 中 seq_item_pull_port 的句柄是 seq_item_port類型。driver代碼用來與sequencer交互的 API 被 seq_item_port 引用,但實(shí)際上是在sequencer seq_item_export 中實(shí)現(xiàn)的(這是標(biāo)準(zhǔn)的 TLM )。

說人話就是握手,driver收了可以不發(fā)rsp,不過這樣sequence就就沒有相關(guān)信息了。然后由于本質(zhì)上還是通過tlm進(jìn)行的,所有通過端口調(diào)用的函數(shù)都是在目標(biāo)端口實(shí)現(xiàn)的,也就是說是在sequencer里實(shí)現(xiàn)的。

UVM Driver API

相關(guān)API包括:

get_next_item

這是一個(gè)阻塞方法,直到sequence用item_done方法把item發(fā)過來。如果在item_done調(diào)用之前,進(jìn)行另一次get_next_item調(diào)用會(huì)導(dǎo)致握手死鎖。

try_next_item

這是 get_next_item() 方法的非阻塞變體。sequencer的item fifo里沒東西就會(huì)返回一個(gè)空句柄,如果有的話,行為就和get_next_item一樣

item_done

是一個(gè)非阻塞方法,應(yīng)該在get_next_item() 或成功的 try_next_item() 調(diào)用之后調(diào)用。如果不傳參數(shù)或者傳一個(gè)空句柄,也可以完成握手,然后sequencer的fifo也不會(huì)有item進(jìn)去,如果傳的不是空句柄的話,就會(huì)送進(jìn)對應(yīng)的fifo。

peek

如果在sequencer的對應(yīng) FIFO 中沒有可用的 REQ sequence_item,peek() 方法將阻塞一直等待,然后返回一個(gè)指向 REQ 對象的指針,執(zhí)行握手的前半部分。在 get() 或 item_done() 調(diào)用之前調(diào)用peek的話,返回的都是同一個(gè)對象。

很好理解,peek本質(zhì)就是不會(huì)影響fifo的內(nèi)容,get和item_done一個(gè)取走item,一個(gè)送入item,自然會(huì)產(chǎn)生影響。

get

和上面的peek對應(yīng),會(huì)影響fifo

put

這個(gè)是一個(gè)非阻塞的方法,可以在任何時(shí)候調(diào)用,和握手機(jī)制沒有關(guān)系。

注意:get_next_item()、get() 和 peek() 方法會(huì)啟動(dòng)sequencer的仲裁,會(huì)從獲得權(quán)限的sequence那里返回一個(gè) sequence_item。

推薦的Driver和sequencer的api調(diào)用模型

對于driver和sequencer之間的item交互,推薦兩種方式

get_next_item() 后跟 item_done()

通過get_next_item,拿到item以后,進(jìn)行處理,最后再使用item_done完成握手。在 item_done() 調(diào)用中不應(yīng)傳遞任何參數(shù)。

這種握手流程是最推薦的,因?yàn)樗苊鞔_的分離了driver和sequencer之間的任務(wù)。

//?
//?Driver?run?method?
//?
task?run_phase(?uvm_phase?phase?);?
?bus_seq_item?req_item;?
?forever?begin?
????seq_item_port.get_next_item(req_item);??//?Blocking?call?returning?the?next?transaction?
??//BFM?handles?all?pin?wiggling?and?population?of?req_item?with?response?data?
??m_bfm.drive(req_item);?
????seq_item_port.item_done();??//?Signal?to?the?sequence?that?the?driver?has?finished?with?the?item?
?end?
endtask:?run?

相應(yīng)的sequence代碼里是 start_item() 后跟一個(gè) finish_item()。由于driver和sequence中的sequence item指向的是同一個(gè)對象,因此可以通過item句柄在sequence內(nèi)使用從driver返回的任何數(shù)據(jù)。換句話說,當(dāng)一個(gè) sequence_item 的句柄作為參數(shù)傳遞給 finish_item() 方法時(shí),driver的 get_next_item() 方法拿到的 sequence_item 所指向的對象是同一個(gè)。當(dāng)driver對 sequence_item 進(jìn)行任何更改時(shí),sequence也能看到item中的所有修改。driver對 item_done() 的調(diào)用會(huì)解除對sequence中的 finish_item() 調(diào)用的阻塞,然后sequence可以訪問 sequence_item 中的字段,包括driver對item修改的那些字段。

//?
//?Sequence?body?method:?
//?
task?body();?
?bus_seq_item?req_item;?
?bus_seq_item?req_item_c;?
?req_item?=?bus_seq_item::type_id::create("req_item");?
?repeat(10)?begin?
????$cast(req_item_c,?req_item.clone);??//?Good?practice?to?clone?the?req_item?item?
??start_item(req_item_c);?
??req_item_c.randomize();?
????finish_item(req_item_c);??//?Driver?has?returned?REQ?with?the?response?fields?updated?
??`uvm_info("body",?req_item_c.convert2string())?
?end?
endtask:?body?

get(req) 之后 put(rsp)

這種流程下,driver通過 get(req) 獲取下一個(gè) sequence_item 并在driver消耗時(shí)間處理 sequence_item 之前一次性發(fā)送回sequence的握手。driver使用 put(rsp) 方法來告訴sequence, sequence_item 已被傳送出去。driver使用響應(yīng)的建議在下一個(gè)章節(jié)中會(huì)提及。

使用這種flow,則sequence在 finish_item() 后面調(diào)用get_response() ,會(huì)阻塞直到驅(qū)動(dòng)程序調(diào)用 put(rsp)。這種使用flow的缺點(diǎn)是在driver實(shí)現(xiàn)起來更復(fù)雜,并且sequence一定要記得處理rsp。

//?
//?run?method?within?the?driver?
//?
task?run_phase(?uvm_phase?phase?);?
?REQ?req_item;??//REQ?is?parameterized?type?for?requests
?RSP?rsp_item;??//RSP?is?parameterized?type?for?responses
?bit?[15:0]?rdata;?
?bit?error;?
?forever?begin?
????seq_item_port.get(req_item);??//?finish_item?in?sequence?is?unblocked?
??//BFM?handles?all?pin?wiggling?and?returns?response?data?as?separate?arguments?
???m_bfm.drive(req_item,?rdata,?error);?
????$cast(rsp_item,?req_item.clone());??//?Create?a?response?transaction?by?cloning?req_item?
????rsp_item.set_id_info(req_item);??//?Set?the?rsp_item?sequence?id?to?match?req_item?
????if(req_item.read_or_write?==?READ)?begin??//?Copy?the?bus?data?to?the?response?fields?
???rsp_item.read_data?=?rdata;?
??end?
??rsp_item.resp?=?error;?
????seq_item_port.put(rsp_item);??//?Handshake?back?to?the?sequence?via?its?get_response()?call?
?end?
endtask?
//?
//?Corresponding?code?within?the?sequence?body?method?
//?
task?body();?
?REQ?req_item;??//REQ?is?parameterized?type?for?requests
?RSP?rsp_item;??//RSP?is?parameterized?type?for?responses
?repeat(10)?begin?
??req_item?=?bus_seq_item::type_id::create("req_item");?
??start_item(req_item);?
??req_item.randomize();?
????finish_item(req_item);??//?This?passes?to?the?driver?get()?call?and?is?returned?immediately?
??get_response(rsp_item);??//?Block?until?a?response?is?received
??`uvm_info("body",?rsp_item.convert2string(),?UVM_LOW);?
?end?
endtask:?body?

將respond item路由回到parent sequence中

當(dāng)有多個(gè)sequence通過sequencer與driver通信時(shí),模型的復(fù)雜度就會(huì)提高。sequencer負(fù)責(zé)將哪個(gè) sequence_item 從哪個(gè)sequence路由到driver中。而當(dāng)driver創(chuàng)建響應(yīng) sequence_item 之后,需要將其路由回正確的sequence。UVM 處理這個(gè)問題的方式是每個(gè) sequence_item 都有一對 id 字段,一個(gè)用于parent sequence,一個(gè)用于標(biāo)識 sequence_item,sequencer 使用這些字段將響應(yīng)路由回parent sequence。作為 start_item() 方法的結(jié)果,請求 sequence_item 具有由sequencer設(shè)置的這些字段,因此,新的響應(yīng) sequence_item 需要獲取請求 ID 信息的副本,以便可以將其路由回原始sequence。為此,在 uvm_sequence_item 基類中提供了 set_id_info() 方法。

意思就是返回rsp的時(shí)候,把id要復(fù)制一下,以便sequencer對于item的正確路由

Driver Response Items 的編碼指南

set_id_info

uvm_sequence_item 有一個(gè) id 字段,該字段由sequencer在sequence調(diào)用 start_item() 的時(shí)候設(shè)置。此 id 幫助sequencer跟蹤每個(gè) sequence_item 關(guān)聯(lián)的sequence,并且此信息用于將響應(yīng)item路由回正確的sequence。盡管在大多數(shù)情況下只有一個(gè)sequence與driver主動(dòng)通信,但這個(gè)機(jī)制是一直運(yùn)行的。sequence_item set_id_info 方法用于把請求item的id復(fù)制給響應(yīng)id。返回響應(yīng)item的時(shí)候,必須設(shè)置id。

如果通過克隆產(chǎn)生或者創(chuàng)建新的響應(yīng)item,就必須要調(diào)用set_id_info給item設(shè)置對應(yīng)的id。

和前面一段的意思是一樣的

使用指針克隆保證安全

當(dāng)一個(gè)響應(yīng)item從driver發(fā)送回一個(gè)sequence時(shí),它的指針將存儲在sequencer的響應(yīng) FIFO 中。如果在發(fā)送下一個(gè)響應(yīng)item指針之前沒有使用響應(yīng)item,除非新的響應(yīng)item指針是針對新對象的,否則兩個(gè)指針都將引用同一個(gè)對象。此問題的一個(gè)常見結(jié)果是連續(xù)讀取 FIFO 會(huì)產(chǎn)生具有相同內(nèi)容的對象。

防止這種情況發(fā)生的方法,推薦克隆響應(yīng)item,以便創(chuàng)建一個(gè)新對象并將指向該新對象的指針傳遞給sequencer響應(yīng) FIFO 或具有不同的請求和響應(yīng)類型。

//?Somewhere?in?a?driver?-?request?and?response?items?
bus_seq_item?req_item;?
bus_seq_item?rsp_item;?
task?run_phase(?uvm_phase?phase?);?
?forever?begin?
??seq_item_port.get(req_item);?
????assert($cast(rsp_item,?req_item.clone());??//?This?does?not?copy?the?id?info?
??rsp_item.set_id_info(req_item);??//?This?sets?the?rsp_item?id?to?thereq_item?id?
//?
//?Do?the?pin?level?transaction,?populate?the?response?fields?
//?
//?Return?the?response:?
??seq_item_port.put(rsp_item);?
//?
??end?
endtask:?run?

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄