Sequence的uvm_do
當一個sequence啟動后會自動執(zhí)行sequence的body任務(wù)。其實,除了body外,還會自動調(diào)用sequence的pre_body與post_body:
4 class my_sequence extends uvm_sequence #(my_transaction);5 my_transaction m_trans;67 function new(string name= "my_sequence");8 super.new(name);9 endfunction1011 virtual task body();12 repeat (10) begin13 `uvm_do(m_trans)14 end15 #1000;16 endtask1718 `uvm_object_utils(my_sequence)19 endclass 每一個sequence都應(yīng)該派生自uvm_sequence,并且在定義時指定要產(chǎn)生的transaction的類型,這里是my_transaction。每一個sequence都有一個body任務(wù),當一個sequence啟動之后,會自動執(zhí)行body中的代碼。在上面的例子中,用到了一個全新的宏:uvm_do。
這個宏是UVM中最常用的宏之一,它用于:
①創(chuàng)建一個my_transaction的實例m_trans;
②將其隨機化;
③最終將其送給sequencer。
如果不使用uvm_do宏,也可以直接使用start_item與finish_item的方式產(chǎn)生transaction,對于初學者來說,使用uvm_do宏即可。
一個sequence在向sequencer發(fā)送transaction前,要先向sequencer發(fā)送一個請求,sequencer把這個請求放在一個仲裁隊列中。
作為sequencer,它需做兩件事情:
第一,檢測仲裁隊列里是否有某個sequence發(fā)送transaction的請求;
第二,檢測driver是否申請transaction。
1)如果仲裁隊列里有發(fā)送請求,但是driver沒有申請transaction,那么sequencer將會一直處于等待driver的狀態(tài),直到driver申請新的transaction。此時,sequencer同意sequence的發(fā)送請求,sequence在得到sequencer的批準后,產(chǎn)生出一個transaction并交給sequencer,后者把這個transaction交給driver。
2)如果仲裁隊列中沒有發(fā)送請求,但是driver向sequencer申請新的transaction,那么sequencer將會處于等待sequence的狀態(tài),一直到有sequence遞交發(fā)送請求,sequencer馬上同意這個請求,sequence產(chǎn)生transaction并交給sequencer,最終driver獲得這個transaction。
3)如果仲裁隊列中有發(fā)送請求,同時driver也在向sequencer申請新的transaction,那么將會同意發(fā)送請求,sequence產(chǎn)生transaction并交給sequencer,最終driver獲得這個transaction。
driver如何向sequencer申請transaction
driver如何向sequencer申請transaction呢?在uvm_driver中有成員變量seq_item_port,而在uvm_sequencer中有成員變量seq_item_export,這兩者之間可以建立一個“通道”,通道中傳遞的transaction類型就是定義my_sequencer和my_driver時指定的transaction類型,在這里是my_transaction,當然了,這里并不需要顯式地指定“通道”的類型,UVM已經(jīng)做好了。在my_agent中,使用connect函數(shù)把兩者聯(lián)系在一起:
31 function void my_agent::connect_phase(uvm_phase phase);32 super.connect_phase(phase);33 if (is_active == UVM_ACTIVE) begin34 drv.seq_item_port.connect(sqr.seq_item_export);35 end36 ap = mon.ap;37 endfunction 當把二者連接好之后,就可以在driver中通過get_next_item任務(wù)向sequencer申請新的transaction:
22 task my_driver::main_phase(uvm_phase phase);23 vif.data <= 8'b0;24 vif.valid <= 1'b0;25 while(!vif.rst_n)26 @(posedge vif.clk);27 while(1) begin28 seq_item_port.get_next_item(req);29 drive_one_pkt(req);30 seq_item_port.item_done();31 end32 endtask
在如上的代碼中,一個最顯著的特征是使用了while(1)循環(huán),因為driver只負責驅(qū)動transaction,而不負責產(chǎn)生,只要有transaction就驅(qū)動,所以必須做成一個無限循環(huán)的形式。這與monitor、reference model和scoreboard的情況非常類似。
通過get_next_item任務(wù)來得到一個新的req,并且驅(qū)動它,驅(qū)動完成后調(diào)用item_done通知sequencer。這里為什么會有一個item_done呢?當driver使用get_next_item得到一個transaction時,sequencer自己也保留一份剛剛發(fā)送出的transaction。當出現(xiàn)sequencer發(fā)出了transaction,而driver并沒有得到的情況時,sequencer會把保留的這份transaction再發(fā)送出去。那么sequencer如何知道driver是否已經(jīng)成功得到transaction呢?如果在下次調(diào)用get_next_item前,item_done被調(diào)用,那么sequencer就認為driver已經(jīng)得到了這個transaction,將會把這個transaction刪除。換言之,這其實是一種為了增yun加可靠性而使用的握手機制。
那么uvm_do怎么握手?
在sequence中,向sequencer發(fā)送transaction使用的是uvm_do宏。這個宏什么時候會返回呢?uvm_do宏產(chǎn)生了一個transaction并交給sequencer,driver取走這個transaction后,uvm_do并不會立刻返回執(zhí)行下一次的uvm_do宏,而是等待在那里,直到driver返回item_done信號。此時,uvm_do宏才算是執(zhí)行完畢,返回后開始執(zhí)行下一個uvm_do,并產(chǎn)生新的transaction。
在實現(xiàn)了driver后,接下來的問題是:sequence如何向sequencer中送出transaction呢?前面已經(jīng)定義了sequence,只需要在某個component(如my_sequencer、my_env)的main_phase中啟動這個sequence即可。以在my_env中啟動為例:
48 task my_env::main_phase(uvm_phase phase);49 my_sequence seq;50 phase.raise_objection(this);51 seq = my_sequence::type_id::create("seq");52 seq.start(i_agt.sqr);53 phase.drop_objection(this);54 endtask 首先創(chuàng)建一個my_sequence的實例seq,之后調(diào)用start任務(wù)。start任務(wù)的參數(shù)是一個sequencer指針,如果不指明此指針,則sequence不知道將產(chǎn)生的transaction交給哪個sequencer。
這里需要引起關(guān)注的是objection,在UVM中,objection一般伴隨著sequence,通常只在sequence出現(xiàn)的地方才提起和撤銷objection。如前面所說,sequence是彈夾,當彈夾里面的子彈用光之后,可以結(jié)束仿真了。
也可以在sequencer中啟動sequence:
task my_sequencer::main_phase(uvm_phase phase);my_sequence seq;phase.raise_objection(this);seq = my_sequence::type_id::create("seq");seq.start(this);phase.drop_objection(this);endtask 在sequencer中啟動與在my_env中啟動相比,唯一區(qū)別是seq.start的參數(shù)變?yōu)榱藅his。
與uvm_do等價的“uvm_create與uvm_send”
除了使用uvm_do宏產(chǎn)生transaction,還可以使用uvm_create宏與uvm_send宏來產(chǎn)生。
class case0_sequence extends uvm_sequence #(my_transaction); virtual task body(); int num = 0; int p_sz; repeat (10) begin num ; `uvm_create(m_trans) assert(m_trans.randomize()); p_sz = m_trans.pload.size(); { m_trans.pload[p_sz - 4], m_trans.pload[p_sz - 3], m_trans.pload[p_sz - 2], m_trans.pload[p_sz - 1]} = num; `uvm_send(m_trans) end endtaskendclass uvm_create宏的作用是實例化transaction。當一個transaction被實例化后,可以對其做更多的處理,處理完畢后使用uvm_send宏發(fā)送出去。這種使用方式比uvm_do系列宏更加靈活。如在上例中,就將pload的最后4個byte替換為此transaction的序號。
事實上,在上述的代碼中,也完全可以不使用uvm_create宏,而直接調(diào)用new進行實例化。
virtual task body();…m_trans = new("m_trans");assert(m_trans.randomize());p_sz = m_trans.pload.size();{m_trans.pload[p_sz - 4],m_trans.pload[p_sz - 3],m_trans.pload[p_sz - 2],m_trans.pload[p_sz - 1]}= num;`uvm_send(m_trans)…endtask
除了uvm_send外,還有uvm_send_pri宏,它的作用是在將transaction交給sequencer時設(shè)定優(yōu)先級:
virtual task body(); … m_trans = new("m_trans"); assert(m_trans.randomize()); p_sz = m_trans.pload.size(); {m_trans.pload[p_sz - 4], m_trans.pload[p_sz - 3], m_trans.pload[p_sz - 2], m_trans.pload[p_sz - 1]} = num; `uvm_send_pri(m_trans, 200) …endtask
與uvm_do等價的“start_item與finish_item”
使用宏來產(chǎn)生transaction。宏隱藏了細節(jié),方便了用戶的使用,但是也給用戶帶來了困擾:宏到底做了什么事情?
不使用宏產(chǎn)生transaction的方式要依賴于兩個任務(wù):start_item和finish_item。在使用這兩個任務(wù)前,必須要先實例化transaction后才可以調(diào)用這兩個任務(wù):
tr = new("tr");start_item(tr);finish_item(tr);
完整使用如上兩個任務(wù)構(gòu)建的一個sequence如下:
virtual task body(); repeat(10) begin tr = new("tr"); start_item(tr); finish_item(tr); endendtask
上述代碼中并沒有對tr進行隨機化??梢栽趖ransaction實例化后、finish_item調(diào)用前對其進行隨機化:
class case0_sequence extends uvm_sequence #(my_transaction); virtual task body(); repeat (10) begin tr = new("tr"); assert(tr.randomize() with {tr.pload.size == 200;}); start_item(tr); finish_item(tr); end endtaskendclass
上述assert語句也可以放在start_item之后、finish_item之前。uvm_do系列宏其實是將下述動作封裝在了一個宏中:
virtual task body(); … tr = new("tr"); start_item(tr); assert(tr.randomize() with {tr.pload.size() == 200;}); finish_item(tr); …endtask
文章內(nèi)容參考自:張強《UVM實戰(zhàn)》