天天看點

UVM實戰(張強)--- UART執行個體代碼詳細注解

目錄

    • 一、整體的設計結構圖
    • 二、各個元件代碼詳解
      • 2.1 DUT
      • 2.2 my_driver
      • 2.3 my_transaction
      • 2.4 my_env
      • 2.5 my_monitor
      • 2.6 my_agent
      • 2.7 my_model
      • 2.8 my_scoreboard
      • 2.9 my_sequencer
      • 2.10 base_test
      • 2.11 my_case0
      • 2.12 my_case1

一、整體的設計結構圖

UVM實戰(張強)--- UART執行個體代碼詳細注解

各個子產品的基礎介紹:

(1)DUT:待測平台,這裡為uart序列槽功能

(2)sequence:建立一個my_straction的執行個體m_trans,将其随機化,最終将其送給sequencer

(3)sequencer:uvm_sequencer就如同一個管道,從這個管道中産生連續的激勵事務,并最終通過TLM端口送至driver一側

(4)driver:該類會從uvm_sequencer中擷取事務(transaction),經過轉化進而在接口中對DUT進行時序激勵

(5)monitor:負責把transaction級别的資料轉變為DUT的端口級别,并驅動給DUT,mointor的行為與其相對,用于收集DUT的端口資料并将其轉化成transaction交給後續的元件如reference model,scoreboard等處理

(6)reference model:reference model用于完成和DUT相同的功能

(7)scoreboard:uvm_scoreboard擔任和SV中的check一樣的功能,即進行資料的對比和報告,這裡要比較的一源于reference model,二源于o_agent的monitor

(8)env:從環境層次結構而言,uvm_env包含多個uvm_agent和其他component,這些不同的元件共同構成一個完整的驗證環境。

uvm_env的角色就是一個結構化的容器,它可以容納其它元件的同時它也可以作為子環境在更高層次集中被嵌入

二、各個元件代碼詳解

2.1 DUT

module dut(clk,rst_n,rxd,rx_dv,txd,tx_en);
input clk;
input rst_n;
input[7:0] rxd;      //rxd接收資料
input rx_dv;         //發送資料有效訓示
output [7:0] txd;    //txd發送資料
output tx_en;        //接收資料有效訓示

reg[7:0] txd;        //always指派用reg
reg tx_en;           //always指派用reg

always @(posedge clk) begin
   if(!rst_n) begin
      txd <= 8'b0;
      tx_en <= 1'b0;
   end
   else begin
      txd <= rxd;    //輸出資料txd會打一拍
      tx_en <= rx_dv;
   end
end
endmodule
           

2.2 my_driver

//(1)`ifndef的使用方法
`ifndef MY_DRIVER__SV 
`define MY_DRIVER__SV
class my_driver extends uvm_driver#(my_transaction); //類的定義

   virtual my_if vif; //(2)virtual的作用

   //(3)工廠機制
   `uvm_component_utils(my_driver) //工廠的注冊
   function new(string name = "my_driver", uvm_component parent = null); //函數的建立
   	  // 所有派生自uvm_driver的類的new函數有兩個參數,一個是string類型的name,一個是uvm_component類型的parent。	
      super.new(name, parent); 
   endfunction

   //(4)build_phase和main_phase
   virtual function void build_phase(uvm_phase phase); 
      super.build_phase(phase);
      //(8)uvm_config_db的用法
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
      //(5)uvm_fatal宏
         `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
   endfunction
	//(7)extern的用法
   extern task main_phase(uvm_phase phase);
   extern task drive_one_pkt(my_transaction tr);
endclass

task my_driver::main_phase(uvm_phase phase);
   vif.data <= 8'b0;
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   while(1) begin
      seq_item_port.get_next_item(req);
      drive_one_pkt(req);
      seq_item_port.item_done();
   end
endtask

task my_driver::drive_one_pkt(my_transaction tr);
   byte unsigned     data_q[];
   int  data_size;
   
   data_size = tr.pack_bytes(data_q) / 8; 
   `uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
   repeat(3) @(posedge vif.clk);
   for ( int i = 0; i < data_size; i++ ) begin
      @(posedge vif.clk);
      vif.valid <= 1'b1;
      vif.data <= data_q[i]; 
   end

   // (6)uvm_info宏。
   @(posedge vif.clk);
   vif.valid <= 1'b0;
   `uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask
`endif
           

(1)如何了解ifndef和define的使用

目的:為了防止同一個檔案在編譯的時候被重複編譯,引起多重定義的問題

即 “if not defined”,也就是說,當檔案編譯到這一行,如果這個檔案還沒有被編譯過,也就是首次編譯,就會執行後續的 `define xxx這句話,把後續的代碼定義一次。反之,則不會再重複編譯。

詳細解釋連結

(2)virtual的作用

1)不使用virtual,父類句柄雖指向子類對象,但調用的仍是父類本身的函數

2)使用virtual,父類句柄指向子類對象,調用的是子類的函數

詳細解釋連結

(3)工廠機制

factory機制的實作被內建在了一個宏中:uvm_component_utils。這個宏所做的事情非常多,其中之一就是将my_driver登記在UVM内部的一張表中,這張表是factory功能實作的基礎。隻要在定義一個新的類時使用這個宏,就相當于把這個類注冊到了這張表中。

(4)build_phase和main_phase

了解build_phase和main_phase build_phase在new函數之後main_phase之前執行 build_phase是一個函數phase,而main_phase是一個任務phase,build_phase是不消耗仿真時間的。build_phase總是在仿真時間($time函數列印出的時間)為0時執行。

(5)uvm_fatal宏

在build_phase中出現了uvm_fatal宏,uvm_fatal宏是一個類似于uvm_info的宏,但是它隻有兩個參數,這兩個參數與uvm_info宏的前兩個參數的意義完全一樣。與uvm_info宏不同的是,當它列印第二個參數所示的資訊後,會直接調用Verilog的finish函數來結束仿真。

(6)uvm_info宏

這個宏的功能與Verilog中display語句的功能類似,但是它比display語句更加強大。它有三個參數,第一個參數是字元串,用于把列印的資訊歸類;第二個參數也是字元串,是具體需要列印的資訊;第三個參數則是備援級别。在驗證平台中,某些資訊是非常關鍵的,這樣的資訊可以設定為UVM_LOW,而有些資訊可有可無,就可以設定為UVM_HIGH,介于兩者之間的就是UVM_MEDIUM。UVM預設隻顯示UVM_MEDIUM或者UVM_LOW的資訊。

(7)extern的用法

extern可以在使得function和task書寫在class的外部,避免class過大難以閱讀

(8)uvm_config_db的用法

uvm_config_db有set和get兩種方法,一般成對出現

set:自頂層向下set,如從top_tb向driver設定參數

  • uvm_config_db#(int)::set(this, “env.agt.drv”, “vif”, 100);

    第一個參數this與第二個參數“env.agt.drv”——構成目标路徑;是以也可以為(this.env, “agt.drv”, “num”, 100)

    第三個參數是傳給該路徑的哪個成員;

    第四個參數是要設定的值。

    get:從底層向頂層get,如從driver中的build_phase使用get。

  • uvm_config_db#(int)::get(this, “”,“vif”, vif);

    第一個參數必須是uvm_component執行個體的指針,第二個參數是相對此執行個體的路徑。一般的,如果第一個參數被設定成this,第二個參數可以是一個空的字元串。

    第三個參數要與set中的第三個參數嚴格一緻。

    第四個參數是要設定的值。

    詳細解釋連結

//列印結果
UVM_INFO my_driver.sv
(20)//指明此條列印資訊的來源,其中括号裡的數字表示原始的uvm_info列印語句在my_driver.sv中的行号。
@48500000 //表明此條資訊的列印時間
:drv[my_driver]data is drived //這是driver在UVM樹中的路徑索引。UVM采用樹形結構,對于樹中任何一個結點,都有一個與其相應的字元串類型的
//路徑索引。路徑索引可以通過get_full_name函數來擷取,把下列代碼加入任何UVM樹的結點中就可以得知目前結點的路徑索引:
           

2.3 my_transaction

`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

//(1)uvm_sequence_item
class my_transaction extends uvm_sequence_item;

   rand bit[47:0] dmac;//48bit的以太網目的位址
   rand bit[47:0] smac;//48bit的以太網源位址
   rand bit[15:0] ether_type;//以太網類型
   rand byte      pload[];//攜帶資料的大小
   rand bit[31:0] crc;//面所有資料的校驗值

   constraint pload_cons{
      pload.size >= 46;
      pload.size <= 1500;
   }

   function bit[31:0] calc_crc();
      return 32'h0;
   endfunction
//(2)post_randomize()
   function void post_randomize();
      crc = calc_crc;
   endfunction

//(3)uvm_object_utils
   `uvm_object_utils_begin(my_transaction)
     //(4)uvm_field_int
      `uvm_field_int(dmac, UVM_ALL_ON)
      `uvm_field_int(smac, UVM_ALL_ON)
      `uvm_field_int(ether_type, UVM_ALL_ON)
      `uvm_field_array_int(pload, UVM_ALL_ON)
      `uvm_field_int(crc, UVM_ALL_ON)
   `uvm_object_utils_end

   function new(string name = "my_transaction");
      super.new();
   endfunction

endclass
`endif
           

(1)uvm_sequence_item

一是my_transaction的基類是uvm_sequence_item。在UVM中,所有的transaction都要從uvm_sequence_item派生,隻有從uvm_sequence_item派生的transaction才可以使用後文講述的UVM中強大的sequence機制。

(2)post_randomize()

post_randomize是SystemVerilog中提供的一個函數,當某個類的執行個體的randomize函數被調用後,post_randomize會緊随其後無條件

地被調用。

(3)uvm_object_utils

二是這裡沒有使用uvm_component_utils宏來實作factory機制,而是使用了uvm_object_utils。從本質上來說,my_transaction與my_driver是有差別的,在整個仿真期間,my_driver是一直存在的,my_transaction不同,它有生命周期。它在仿真的某一時間産生,經過driver驅動,再經過reference model處理,最終由scoreboard比較完成後,其生命周期就結束了。一般來說,這種類都是派生自uvm_object或者uvm_object的派生類,uvm_sequence_item的祖先就是uvm_object。UVM中具有這種特征的類都要使用uvm_object_utils宏來實作。

(4)uvm_field_int

uvm中的宏方法。

2.4 my_env

`ifndef MY_ENV__SV
`define MY_ENV__SV
//(1)為什麼要使用my_env
class my_env extends uvm_env;

   my_agent   i_agt;
   my_agent   o_agt;
   my_model   mdl;
   my_scoreboard scb;
   //(4)uvm_tlm_analysis_fifo的用法
   uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo;
   uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
   uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo;
   
   function new(string name = "my_env", uvm_component parent);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      //(2)工廠建立對象的方式
      i_agt = my_agent::type_id::create("i_agt", this);
      o_agt = my_agent::type_id::create("o_agt", this);
      i_agt.is_active = UVM_ACTIVE;
      o_agt.is_active = UVM_PASSIVE;
      mdl = my_model::type_id::create("mdl", this);
      scb = my_scoreboard::type_id::create("scb", this);
      agt_scb_fifo = new("agt_scb_fifo", this);
      agt_mdl_fifo = new("agt_mdl_fifo", this);
      mdl_scb_fifo = new("mdl_scb_fifo", this);

   endfunction

   extern virtual function void connect_phase(uvm_phase phase);
   //(3)uvm_component_utils的位置
   `uvm_component_utils(my_env)
endclass

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   i_agt.ap.connect(agt_mdl_fifo.analysis_export);
   mdl.port.connect(agt_mdl_fifo.blocking_get_export);
   mdl.ap.connect(mdl_scb_fifo.analysis_export);
   scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
   o_agt.ap.connect(agt_scb_fifo.analysis_export);
   scb.act_port.connect(agt_scb_fifo.blocking_get_export); 
endfunction

`endif
           

(1)為什麼使用my_env

在驗證平台中加入reference model、scoreboard等之前,思考一個問題:假設這些元件已經定義好了,那麼在驗證平台的什麼位置對它們進行執行個體化呢?在top_tb中使用run_test進行執行個體化顯然是不行的,因為run_test函數雖然強大,但也隻能執行個體化一個執行個體;如果在top_tb中使用2.2.1節中執行個體化driver的方式顯然也不可行,因為run_test相當于在top_tb結構層次之外建立一個新的結構層次,而2.2.1節的方式則是基于top_tb的層次結構,如果基于此進行執行個體化,那麼run_test的引用也就沒有太大的意義了;如果在driver中進行執行個體化則更加不合理。

(2)工廠建立對象的方式

這裡沒有直接調用my_driver的new函數,而是使用了一種古怪的方式。這種方式就是factory機制帶來的獨特的執行個體化方式。隻有使用factory機制注冊過的類才能使用這種方式執行個體化;隻有使用這種方式執行個體化的執行個體,才能使用後文要講述的factory機制中最為強大的重載功能。驗證平台中的元件在執行個體化時都應該使用type_name::type_id::create的方式。

(3)uvm_component_utils的位置

uvm_componnet_utils常定義在class的最前方,這裡放在class的最後不影響結果。

2.5 my_monitor

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
class my_monitor extends uvm_monitor;//所有的monitor都應該派生自uvm_monitor

   virtual my_if vif;
   uvm_analysis_port #(my_transaction)  ap;
   
   `uvm_component_utils(my_monitor)
   function new(string name = "my_monitor", uvm_component parent = null);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
         `uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
      ap = new("ap", this);
   endfunction

   extern task main_phase(uvm_phase phase);
   extern task collect_one_pkt(my_transaction tr);
endclass

task my_monitor::main_phase(uvm_phase phase);
   my_transaction tr;
   while(1) begin
      tr = new("tr");
      collect_one_pkt(tr);
      ap.write(tr);
   end
endtask

task my_monitor::collect_one_pkt(my_transaction tr);
   byte unsigned data_q[$];
   byte unsigned data_array[];
   logic [7:0] data;
   logic valid = 0;
   int data_size;
   
   while(1) begin
      @(posedge vif.clk);
      if(vif.valid) break;
   end
   
   `uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
   while(vif.valid) begin
      data_q.push_back(vif.data);
      @(posedge vif.clk);
   end
   data_size  = data_q.size();   
   data_array = new[data_size];
   for ( int i = 0; i < data_size; i++ ) begin
      data_array[i] = data_q[i]; 
   end
   tr.pload = new[data_size - 18]; //da sa, e_type, crc
   data_size = tr.unpack_bytes(data_array) / 8; 
   `uvm_info("my_monitor", "end collect one pkt", UVM_LOW);
endtask
`endif
           

有幾點需要注意的是:

第一,所有的monitor類應該派生自uvm_monitor;

第二,與driver類似,在my_monitor中也需要有一個virtual my_if;

第三,uvm_monitor在整個仿真中是一直存在的,是以它是一個component,要使用uvm_component_utils宏注冊;

第四,由于monitor需要時刻收集資料,永不停歇,是以在main_phase中使用while(1)循環來實作這一目的。

2.6 my_agent

`ifndef MY_AGENT__SV
`define MY_AGENT__SV
//(1)為什麼會有agent這個子產品
class my_agent extends uvm_agent ;
   my_sequencer  sqr;
   my_driver     drv;
   my_monitor    mon;
   
   uvm_analysis_port #(my_transaction)  ap;
   
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction 
   
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual function void connect_phase(uvm_phase phase);

   `uvm_component_utils(my_agent)
endclass 


function void my_agent::build_phase(uvm_phase phase);
   super.build_phase(phase);
   //(2)如何了解is_active變量
   if (is_active == UVM_ACTIVE) begin
      sqr = my_sequencer::type_id::create("sqr", this);
      drv = my_driver::type_id::create("drv", this);
   end
   mon = my_monitor::type_id::create("mon", this);
endfunction 

function void my_agent::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   if (is_active == UVM_ACTIVE) begin
      drv.seq_item_port.connect(sqr.seq_item_export);
   end
   ap = mon.ap;
endfunction
`endif
           

(1)為什麼會有agent這個子產品?

driver和monitor兩者之間的代碼高度相似。其本質是因為二者處理的是同一種協定,在同樣一套既定的規則下做着不同的事情。由于二者的這種相似性,UVM中通常将二者封裝在一起,成為一個agent。是以,不同的agent就代表了不同的協定。

(2)如何了解is_active變量

is_active有兩種模式,分别為UVM_PASSIVE和UVM_ACTIVE,is_active預設值為UVM_ACTIVE,這種模式下,是需要例化driver的。那麼什麼情況下需要UVM_PASSIVE模式呢?下圖中,在輸出端口上不需要驅動任何信号,隻需要監測信号。在這種情況下,端口上是隻需要monitor的,是以driver可以不用執行個體化。

UVM實戰(張強)--- UART執行個體代碼詳細注解

2.7 my_model

`ifndef MY_MODEL__SV
`define MY_MODEL__SV

class my_model extends uvm_component;
   
   uvm_blocking_get_port #(my_transaction)  port;
   (1)uvm_analysis_port的使用方法
   uvm_analysis_port #(my_transaction)  ap;

   extern function new(string name, uvm_component parent);
   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);//extern外部定義寫了virtual,下面不需要再寫virtual

   `uvm_component_utils(my_model)
endclass 

function my_model::new(string name, uvm_component parent);
   super.new(name, parent);
endfunction 

function void my_model::build_phase(uvm_phase phase);
   super.build_phase(phase);
   port = new("port", this);
   ap = new("ap", this);
endfunction

task my_model::main_phase(uvm_phase phase);
   my_transaction tr;
   my_transaction new_tr;
   super.main_phase(phase);
   while(1) begin
      port.get(tr);
      new_tr = new("new_tr");
      new_tr.copy(tr);
      `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
      new_tr.print();
      ap.write(new_tr);
   end
endtask
`endif
           

(1)uvm_analysis_port的使用方法

在UVM中,通常使用TLM(Transaction Level Modeling)實作component之間transaction級别的通信。要實作通信,有兩點是值得考慮的:第一,資料是如何發送的?第二,資料是如何接收的?在UVM的transaction級别的通信中,資料的發送有多種方式,其中一種是使用uvm_analysis_port。uvm_analysis_port是一個參數化的類,其參數就是這個analysis_port需要傳遞的資料的類型,也就是uvm_analysis_port #(my_transaction) ap;裡面括号的内容my_transaction。

2.8 my_scoreboard

`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV
class my_scoreboard extends uvm_scoreboard;
   my_transaction  expect_queue[$];
   //(1)exp_port和act_port
   uvm_blocking_get_port #(my_transaction)  exp_port;
   uvm_blocking_get_port #(my_transaction)  act_port;
   `uvm_component_utils(my_scoreboard) //不加封号

   extern function new(string name, uvm_component parent = null);
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual task main_phase(uvm_phase phase);
endclass 

function my_scoreboard::new(string name, uvm_component parent = null);
   super.new(name, parent);
endfunction 

//(2)build_phase
function void my_scoreboard::build_phase(uvm_phase phase);
   super.build_phase(phase);
   exp_port = new("exp_port", this);
   act_port = new("act_port", this);
endfunction 

task my_scoreboard::main_phase(uvm_phase phase);
   my_transaction  get_expect,  get_actual, tmp_tran;
   bit result;
 
   super.main_phase(phase);
   fork 
      while (1) begin
         exp_port.get(get_expect);
         //(3)expect_queue
         expect_queue.push_back(get_expect);
      end
      while (1) begin
         act_port.get(get_actual);
         if(expect_queue.size() > 0) begin
         	//(3)expect_queue
            tmp_tran = expect_queue.pop_front();
            //(4)compare
            result = get_actual.compare(tmp_tran);
            if(result) begin 
               `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);//這裡uvm_info後面加了封号,但是有些書上沒加封号,應該是都可以,不影響。
            end
            else begin
               `uvm_error("my_scoreboard", "Compare FAILED");
               $display("the expect pkt is");
               tmp_tran.print();
               $display("the actual pkt is");
               get_actual.print();
            end
         end
         else begin
         //(5)uvm_error
            `uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");
            $display("the unexpected pkt is");
            get_actual.print();
         end 
      end
   join
endtask
`endif
           

(1)exp_port和act_port

scoreboard的兩個比較值一個來自于reference model,用exp_port擷取,另外一個來自于o_agt,用act_port擷取。

(2)main_phase

在main_phase中通過fork建立起了兩個程序,一個程序處理exp_port的資料,;另外一個程序處理act_port的資料,這是DUT的輸出資料。

(3)expect_queue

當收到資料後,把資料放入expect_queue中,當收集到這些資料後,從expect_queue中彈出之前從exp_port收到的資料,并調用my_transaction的my_compare函數。

(4)compare

my_compare逐字比較兩個my_transaction。

(5)uvm_error

UVM_ERROR的預設處理方式為UVM_DISPLAY|UVM_COUNT,将消息輸出到标準出口端,增加退出計劃變量quit_count,當quit_count達到一定數量時,停止仿真。

2.9 my_sequencer

`ifndef MY_SEQUENCER__SV
`define MY_SEQUENCER__SV

class my_sequencer extends uvm_sequencer #(my_transaction);
   
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction 
   
   `uvm_component_utils(my_sequencer)
endclass

`endif
           

sequencer的定義非常簡單,派生自uvm_sequencer,并且使用uvm_component_utils宏來注冊到factory中。uvm_sequencer是一個

參數化的類,其參數是my_transaction,即此sequencer産生的transaction的類型。

2.10 base_test

`ifndef BASE_TEST__SV
`define BASE_TEST__SV

class base_test extends uvm_test;

   my_env         env;//?
   
   function new(string name = "base_test", uvm_component parent = null);
      super.new(name,parent);
   endfunction
   
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual function void report_phase(uvm_phase phase);
   `uvm_component_utils(base_test)
endclass


function void base_test::build_phase(uvm_phase phase);
   super.build_phase(phase);
   env  =  my_env::type_id::create("env", this); 
endfunction

function void base_test::report_phase(uvm_phase phase);
   uvm_report_server server;
   int err_num;
   super.report_phase(phase);

   server = get_report_server();
   err_num = server.get_severity_count(UVM_ERROR);

   if (err_num != 0) begin
      $display("TEST CASE FAILED");
   end
   else begin
      $display("TEST CASE PASSED");
   end
endfunction

`endif
           

base_test派生自uvm_test,使用uvm_component_utils宏來注冊到factory中

2.11 my_case0

`ifndef MY_CASE0__SV
`define MY_CASE0__SV
class case0_sequence extends uvm_sequence #(my_transaction);

   my_transaction m_trans;

   function  new(string name= "case0_sequence");
      super.new(name);
   endfunction 
   
   virtual task body();
      if(starting_phase != null) 
      	//(1)raise_objection
         starting_phase.raise_objection(this);
      repeat (10) begin
         `uvm_do(m_trans)
      end
      #100;
      if(starting_phase != null) 
      	//(1)drop_objection
         starting_phase.drop_objection(this);
   endtask

   `uvm_object_utils(case0_sequence)
endclass

class my_case0 extends base_test;

   function new(string name = "my_case0", uvm_component parent = null);
      super.new(name,parent);
   endfunction 
   extern virtual function void build_phase(uvm_phase phase); 
   `uvm_component_utils(my_case0)
endclass

function void my_case0::build_phase(uvm_phase phase);
   super.build_phase(phase);

   uvm_config_db#(uvm_object_wrapper)::set(this, 
                                           "env.i_agt.sqr.main_phase", 
                                           "default_sequence", 
                                           case0_sequence::type_id::get());
endfunction

`endif
           

(1)raise_objection和drop_objection

在每個phase中,UVM會檢查是否有objection被提起(raise_objection),如果有,那麼等待這個objection被撤銷(drop_objection)後停止仿真;如果沒有,則馬上結束目前phase。

在開始學習時,讀者可以簡單地将drop_objection語句當成是finish函數的替代者,隻是在drop_objection語句之前必須先調用

raise_objection語句,raise_objection和drop_objection總是成對出現。

raise_objection語句必須在main_phase中第一個消耗仿真時間 [1]的語句之前。如$display語句是不消耗仿真時間的,這些語句可

以放在raise_objection之前,但是類似@(posedge top.clk)等語句是要消耗仿真時間的。

2.12 my_case1

`ifndef MY_CASE1__SV
`define MY_CASE1__SV
class case1_sequence extends uvm_sequence #(my_transaction);
   my_transaction m_trans;

   function  new(string name= "case1_sequence");
      super.new(name);
   endfunction 

   virtual task body();
      if(starting_phase != null) 
         starting_phase.raise_objection(this);
      repeat (10) begin
         `uvm_do_with(m_trans, { m_trans.pload.size() == 60;})
      end
      #100;
      if(starting_phase != null) 
         starting_phase.drop_objection(this);
   endtask

   `uvm_object_utils(case1_sequence)
endclass

class my_case1 extends base_test;
  
   function new(string name = "my_case1", uvm_component parent = null);
      super.new(name,parent);
   endfunction 
   
   extern virtual function void build_phase(uvm_phase phase); 
   `uvm_component_utils(my_case1)
endclass

function void my_case1::build_phase(uvm_phase phase);
   super.build_phase(phase);

   uvm_config_db#(uvm_object_wrapper)::set(this, 
                                           "env.i_agt.sqr.main_phase", 
                                           "default_sequence", 
                                           case1_sequence::type_id::get());
endfunction

`endif
           

uvm_do_with宏,它是uvm_do系列宏中的一個,用于在随機化時提供對某些字段的限制

UVM實戰(張強)--- UART執行個體代碼詳細注解
UVM實戰(張強)--- UART執行個體代碼詳細注解

繼續閱讀