UVM实现component之间transaction级别的通信

UVM实现component之间transaction级别的通信_第1张图片
        my_model是从i_agt中得到my_transaction,并把 my_transaction传递给my_scoreboard。在UVM中,通常使用TLM(Transaction Level Modeling)实现component之间transaction级别 的通信

        在UVM的transaction级别的通信 中,数据的发送有多种方式,其中一种是使用uvm_analysis_port。

(1)使用uvm_analysis_port发送数据

        在my_monitor中定义如下变量:

my_monitor.sv

uvm_analysis_port #(my_transaction) ap;/// 声明了ap

        uvm_analysis_port是一个参数化的类,其参数就是这个analysis_port需要传递的数据的类型,在例中是my_transaction。
         声明了ap后,需要在monitor的build_phase中将其实例化:

my_monitor.sv

   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);///在monitor的build_phase中将其实例化
   endfunction

        在main_phase中,当收集完一个transaction后,需要将其写入ap中:

task my_monitor::main_phase(uvm_phase phase);
    my_transaction tr;
        while(1) begin
            tr = new("tr");
            collect_one_pkt(tr);
            ap.write(tr);当收集完一个transaction后,需要将其写入ap中
    end
endtask

        write是uvm_analysis_port的一个内建函数。到此,在my_monitor中需要为transaction通信准备的工作已经全部完成。

(2)使用uvm_blocking_get_port接收数据

        UVM的transaction级别通信的数据接收方式也有多种,其中一种就是使用uvm_blocking_get_port。这也是一个参数化的类,其 参数是要在其中传递的transaction的类型

        在my_model的第6行中,定义了一个端口,并在build_phase中对其进行实例化。

my_model.sv

   uvm_blocking_get_port #(my_transaction)  port;//定义端口


function void my_model::build_phase(uvm_phase phase);
   super.build_phase(phase);
   port = new("port", this);  //在build_phase中对其进行实例化
   ap = new("ap", this);
endfunction

        在 main_phase中,通过port.get任务来得到从i_agt的monitor中发出的transaction

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);//在 main_phase中,
                    //通过port.get任务来得到从i_agt的monitor中发出的transaction

      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

        在my_monitor和my_model中定义并实现了各自的端口之后,通信的功能并没有实现,还需要在my_env中使用fifo将两个端口 联系在一起。在my_env中定义一个fifo,并在build_phase中将其实例化

my_env.sv

uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      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);

        //实例化agt_mdl_fifo
      agt_mdl_fifo = new("agt_mdl_fifo", this);


   endfunction

        fifo的类型是uvm_tlm_analysis_fifo,它本身也是一个参数化的类,其参数是存储在其中的transaction的类型,这里是 my_transaction
        之后,在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

        这里引入了connect_phase。与build_phase及main_phase类似,connect_phase也是UVM内建的一个phase,它在build_phase执行 完成之后马上执行。但是与build_phase不同的是,它的执行顺序并不是从树根到树叶,而是从树叶到树根——先执行driver和 monitor的connect_phase,再执行agent的connect_phase,最后执行env的connect_phase
         为什么这里需要一个fifo呢?不能直接把my_monitor中的analysis_port和my_model中的blocking_get_port相连吗?由于 analysis_port是非阻塞性质的,ap.write函数调用完成后马上返回,不会等待数据被接收。假如当write函数调用时, blocking_get_port正在忙于其他事情,而没有准备好接收新的数据时,此时被write函数写入的my_transaction就需要一个暂存的位 置,这就是fifo。
        在如上的连接中,用到了i_agt的一个成员变量ap,它的定义与my_monitor中ap的定义完全一样:

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 

        与my_monitor中的ap不同的是,不需要对my_agent中的ap进行实例化,而只需要在my_agent的connect_phase中将monitor的值 赋给它,换句话说,这相当于是一个指向my_monitor的ap的指针

my_agent.sv

function void my_agent::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   ap = mon.ap;
endfunction

        根据前面介绍的connect_phase的执行顺序,my_agent的connect_phase的执行顺序早于my_env的connect_phase的执行顺序,从 而可以保证执行到i_agt.ap.connect语句时,i_agt.ap不是一个空指针。

你可能感兴趣的:(uvm,Systemverilog,uvm)