1.reference model完成和DUT相同的功能:

`ifndef MY_MODEL__SV
`define MY_MODEL__SV

class my_model extends uvm_component;
   
   uvm_blocking_get_port #(my_transaction)  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);

   `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.my_copy(tr);
      `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
      new_tr.my_print();
      ap.write(new_tr);
   end
endtask
`endif


1)line 6:从前级monitor 得到transaction 的接口。

2)line 7:发送给后级scoreboard 的接口。

3)new只是my_model 本身。里面的两个 port 实在build_phase 中做的。

4)line 27,tr只是声明了句柄,并没有new 占用内存空间,line 31只是把monitor得到的tr(占内存) 的地址赋给model 这个句柄。(猜的,应该是这样。get只不过得到个指针)

5)line 28 声明的new_tr 在line32被 new,说明占用内存了,line 33进而copy 到这个内存。

6)line36是把赋值得到的new_tr,给后面的scoreboard。


2.my_copy 是在my_transaction中定义的函数,如下:

function void my_copy(my_transaction tr);
      if(tr == null)
         `uvm_fatal("my_transaction", "tr is null!!!!")
      dmac = tr.dmac;
      smac = tr.smac;
      ether_type = tr.ether_type;
      pload = new[tr.pload.size()];
      for(int i = 0; i < pload.size(); i++) begin
         pload[i] = tr.pload[i];
      end
      crc = tr.crc;
   endfunction



3.需要在my_env中例化,如下:

class my_env extends uvm_env;
************
   my_model  mdl;
***********
   
   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);
************
      mdl = my_model::type_id::create("mdl", this);
**********
   endfunction

   extern virtual function void connect_phase(uvm_phase phase);
   
   `uvm_component_utils(my_env)
endclass

function void my_env::connect_phase(uvm_phase phase);
******************
endfunction



4.UVM树变为:

UVM:2.3 为验证平台加入各个组件->2.3.5 加入reference model_句柄

5.在UVM中,通常使用TLM实现component 之间transaction 级别的通信。有两点要考虑:如何发送,如何接受。发送有多种方式,其中一种是使用uvm_analysis_port。

1)在my_monitor定义如下:

class my_monitor extends uvm_monitor;
**************

   uvm_analysis_port #(my_transaction)  ap;


2)声明ap后,build中例化:

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

new只是monitor本身,里面的port (ap)需要在build_phase中实现。



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

1). write 是uvm_analysis_port 的一个内建函数。到此,my_monitor为transaction通信准备的工作已全部完成。



6.接受方式也有多种。其中之一使用uvm_blocking_get_port。上面的monitor中,通过port.get 来得到从i_agt 的monitor中发出的transaction。

7.在my_monitor 和my_model 中各自定义并实现接口后,还要在my_env中使用fifo将两个端口连接到一起(在env中因为:看树结构图)。在my_env定义一个fifo,在build_phase例化:

class my_env extends uvm_env;

   uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;



virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      ****
      agt_mdl_fifo = new("agt_mdl_fifo", this);
   endfunction



8.在env 的connect_phase中,将fifo的两侧的口,分别与my_monitor中的analysis_port 和 my_model 中的blocking_get_port相连:

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);
endfunction



1)connect_phase也是UVM内建的一个phase。在build_phase后马上执行。但connect_phase是从树叶到树根。

2)之前ap例化在monitor中,但上面代码是i_agt和fifo 相连。my_agent 也声明个句柄,但不例化,当指针用,指向my_monitor的ap。

3)用fifo而不直接连接的原因是:analysis_port是非阻塞的,ap.write调用完后马上返回,不会等待数据被接收。加入write调用时,blocking_get_port忙于其他事,此时被write写入的my_transaction需要一个暂存的位置,就是fifo。(深度呢???)

4)my_agent的connect_phase 执行顺序早于my_env 的connect_phase,可以保证env中连接的时候,i_agt.ap已经被赋值,不是一个空指针。


9.上面的连接中,用的是i_agt 的 ap,定义和my_monitor完全一样,但是不用例化,需在my_agent 的connect_phase中将monitor 的ap赋值给它,相当于指向my_monitor的ap 的指针,my_agent 如下:

`ifndef MY_AGENT__SV
`define MY_AGENT__SV

class my_agent extends uvm_agent ;
   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);
   if (is_active == UVM_ACTIVE) begin
       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);
   ap = mon.ap;//<================
endfunction

`endif



10. 整体结构如下图:

UVM:2.3 为验证平台加入各个组件->2.3.5 加入reference model_句柄_02

可以看出现uvm 的一些明明规则:

1)components 输出都叫analysis_port(往外给的),输入都叫uvm_blocking_get_port(往回拿的),还有其他的TLM。

2)上面的名字和fifo都是对应的,analysis 连到analysis,blocking_get 链接到blocking_get。

3)components的都叫port,fifo 的都叫export。