UVM-TLM通信机制(四)

TLM(transaction level modeling)是一个基于事务(transaction)的通信方式,通常在高抽象级的语言中被引用作为模块之间的通讯方式,例如SystemC或者UVM。TLM通信需要两个通信的对象,这两个对象分别称之为initiator和target。

一、两对象与三端口

1.1.两个通信对象:initiator、target

  • initiator:通信请求的发起方,属于initiator;
  • target : 通信请求的响应方,谁就属于target;

注意:通信发起方并不代表了transaction的流向起点,即不一定数据是从initiator流向target,也可能是从target流向了initiator。因此,按照transaction的流向,我们又可以将两个对象分为producerconsumer。区分它们的方法:

  • producer:生产数据方,即为producer;
  • consumer:接收数据方,即为consumer;img

1.2.三个通信端口:port、export、imp

通信端口按照类型可以划分为三种:

  1. port:通信请求方initiator的发起端,initiator凭借port端口才可以访问target。
  2. export:作为initiator和target中间层次的端口
  3. imp:只能作为target接收请求的响应端,它无法作为中间层次的端口,所以imp的连接无法再次延伸。

端口优先级:port > export > imp, 使用connect()建立连接关系时,只有优先级高的才能调用connect()做连接,即port可以连接port、export或者imp;export可以连接export或者imp;imp只能作为数据传送的重点,无法扩展连接。三种端口非是uvm_component的子类,应该使用new()函数在build_phase中创建。(注意:不能用create创建,端口不属于UVM树的一部分)。

二、一对一传输

按通信传输的方向可以分为单向(unidirection)和双向(bidirection)。
需要说明的是,不论是单向传输还是双向传输都有阻塞和非阻塞之分。下面以阻塞传输为中心。

  • 单向传输:由initiator发起request transaction,数据有生产方producer发送到数据接收方consumer。
  • 双向传输:由initiator发起request transaction,传送至target;而target在消化了request transaction后,也会发起response transaction,继而返回给initiator。数据的流向是双向的。
  • 所有常用的一对一Port类型的总结:

img

2.1.单向传输——put( )和get( )操作传输

2.1.1. Put( )型操作传输

  • 单向通信中,port和export端口的参数只有一个;imp端口,参数有两个,第一个是传递的item类,第二个是实现该端口的component
  • 阻塞的传输方式通过blocking的前缀来作为函数名的一部分,而非阻塞的方式则名为nonblocking
  • 端口的实例化 使用new()函数在build_phase中创建
  • 阻塞的方法类型为task,这保证了可以实现等待事件和延时;非阻塞的方法类型为function,这确保了方法调用可以立刻返回。
  • 发起者的动作实现,最终会落到终点IMP所在的component中,因此必须 在imp所在的component中定义名字为put/get/transport的函数或任务,完成最终的数据传输操作。

在这里插入图片描述

class transaction extends uvm_transaction;      //传输数据包
   int id;
   int data;
   ...
endclass

class producer extends uvm_component;            //数据生产方producer
   ...
   uvm_blocking_put_port#(transaction)     put_port;     //定义端口
   function void build_phase(uvm_phase phase);
      put_port = new("put_port",this);               //创建实例化端口
   endfunction
   
   task run_phase(uvm_phase phase);        
      put_port.put(tr);        //通过数据接收方consumer提供的任务put()处理数据,然后通过TLM传输数据过去
   endtask
endclass

class consumer extends uvm_component;            //数据接收方consumer
   ...
   uvm_blocking_put_imp#(transaction, consumer)     put_imp;     //定义端口
   function void build_phase(uvm_phase phase);
      put_imp = new("put_imp",this);               //创建实例化端口
   endfunction
   
   virtual task put(transaction tr);        //数据生产方producer会调用接收方consumer定义的put()任务
      process(tr);        //通过数据接收方consumer提供的任务put()处理数据
   endtask
endclass

class environment extends uvm_env;       //环境层
   ...
   producer    p;    //数据生产方
   consumer    c;    //数据接收方
   ...
   virtual function void connect_phase(uvm_phase phase);
      p.put_port.connect(c.put_imp);       //建立连接
   endfunction
endclass

1)、上述代码分别在producer和consumer中声明并例化了两个端口实例:

uvm_blocking_put_port #(transaction)        put_port;
     uvm_blocking_put_imp #(transaction,consumer)   put_imp;//第一个是传递的item类,第二个是实现该端口的component**;

2)、在environment环境对producer与consumer进行连接之前,需要在consumer中实现两个端口对应的方法
    virtual task put( transaction  tr );    //阻塞端口需要定义为任务task
3)、最后在environment中对两个组件之间的端口进行了连接,这使得producer的run phase中可以通过自身的两个端口间接调用consumer中的方法。需要注意的是,在调用方法之前的几个步骤是必不可少的:

– 定义端口
     – 实现对应方法
     – 在上层将端口进行连接

2.1.2.Get( )型操作传输

img

数据请求由consumer主动发起,通过调用producer中的get函数建立起连接。红色为port端口,绿色为imp端口。事务传输方向为箭头指向方向。

class transaction extends uvm_transaction;      //传输数据包
   int id;
   int data;
   ...
endclass

class producer extends uvm_component;            //数据生产方producer
   ...
   uvm_blocking_get_imp#(transaction,producer)     get_imp;     //定义端口
   function void build_phase(uvm_phase phase);
      get_imp = new("put_imp",this);               //创建实例化端口
   endfunction
    
   virtual task get(output transaction tr);        //在imp端口所在component中定义get()任务或者方法,output方向
      tr = transaction::type_id::create("tr",this);     
   endtask
endclass

class consumer extends uvm_component;            //数据接收方consumer
   ...
   uvm_blocking_get_port#(transaction, consumer)     get_port;     //定义端口
   function void build_phase(uvm_phase phase);
      put_imp = new("put_imp",this);               //创建实例化端口
   endfunction

   task run_phase(uvm_phase phase);        
      get_port.get(tr);        //通过imp所在component定义的get()处理数据,然后通过TLM传输数据
   endtask   
endclass

class environment extends uvm_env;       //环境层
   ...
   producer    p;    //数据生产方
   consumer    c;    //数据接收方
   ...
   virtual function void connect_phase(uvm_phase phase);
      c.get_port.connect(p.get_imp);       //建立连接
   endfunction
endclass

2.2.transport( )操作传输

双向传输通信通过transport()任务实现。相当于一次put传输加一次get传输,可以在同一任务调用过程中完成REQ和RSP的发出和返回;

img

在示意图上常常用方框表示Port,圆圈表示Export。

其他TLM操作还有peek,get_peek两种,但实际应用较少。

2.3.跨层次传输——uvm_analysis_port

在这里插入图片描述

在复杂的UVM TB中常用到port跨多级component连接的情形,在多层次连接中,connect要按控制流逐层进行连接。在一个path中,始终只会有一个imp作为连接的结尾。

class monitor extends uvm_monitor;
   ...
   uvm_analysis_port#(transaction)    analysis_port;     //声明端口
   virtual function void build_phase(uvm_phase phase);
      this.analysis_port=new("analysis_port",this);    //端口实例化
   endfunction
endclass

class agent extends uvm_agent;
   ...
   monitor   mon;
   uvm_analysis_port#(transaction)    analysis_port;    //声明端口
   virtual function void build_phase(uvm_phase phase);
      this.analysis_port=new("analysis_port",this);    //端口实例化
   endfunction

   virtual function void connect_phase(uvm_phase phase);
      mon.analysis_port.connect(this.analysis_port);     //相同端口的跨层次连接
   endfucntion
endclass

class environment extends uvm_env;
   ...
   agent  agt;
   scoreboard  sb;
   ...
   virtual function void connect_phase(uvm_phase phase);
      agt.analysis_port.connect(sb.analysis_imp);     //不同端口类型连接
   endfucntion
endclass    

三、一对多传输

前面通信有一个共同的地方即都是端对端的方式,同时在target一端需要实现传输方法,例如put()或者get()。这种方式在实际使用过程中也不免会给用户带来一些烦恼:

  • 如何可以不自己实现这些传输方法,同时可以享受到TLM的好处
  • 对于monitor、coverage collector等组件在传输数据时,会存在一端到多端的传输,如何解决这一问题。

3.1.fifo模式

uvm_tlm_fifo类是一个新的组件,它继承于uvm_component,且预先内置了多个端口、实现了多个对应方法供用户使用;功能类似mailbox #(T),该邮箱没有尺寸限制,用来存储数据类型T,而uvm_tlm_fifo的多个端口对应的方法都是利用该邮箱实现了数据读写。uvm_tlm_fifo所带的端口类型都是imp!!!

img

fifo之前采用put模式接口,后端采用get模式接口。

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
   componentA compA;
   componentB compB;
 
   uvm_tlm_fifo #(transaction)    tlm_fifo;//定义TLM FIFO
   ... 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      compA = componentA::type_id::create ("compA", this);
      compB = componentB::type_id::create ("compB", this);
 
      tlm_fifo = new ("tlm_fifo", this, 2);    //创建一个深度为2的FIFO,2省略,FIFO无限深
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      compA.put_port.connect (tlm_fifo.put_export);
      compB.get_port.connect (tlm_fifo.get_export);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      forever begin
         #10 if (tlm_fifo.is_full ())//uvm_tlm_fifo常用的方法见UVM手册,FIFO为满时打印信息
               `uvm_info ("UVM_TLM_FIFO", "Fifo is now FULL !", UVM_MEDIUM)
      end
   endtask
endclass

在这里插入图片描述

monitor与modle之间的通信通过fifo模式,agent的端口为export为中间级。出現了跨层次传输,agent为中间层也需要设置端口。

3.2.分析端口——Analysis port(一对多传输、最常用)

UVM提供了一种分析端口(analysis port),它在组件中是以广播(broadcast)的形式向外发送数据的,而不管存在几个imp或者没有imp。分析端口的根据端口类型的不同分为:

  • uvm_analysis_port、 uvm_analysis_export、 uvm_analysis_imp
  • 只有一个操作:write()
class componentB extends uvm_component;
   ...
   uvm_analysis_port #(transaction) ap;        //1.定义分析port端口
   ...
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      ap = new ("analysis_port", this);        //2.创建端口对象
   endfunction
 
  virtual task run_phase (uvm_phase phase);
    super.run_phase (phase); 
    for (int i = 0; i < 5; i++) begin
        simple_packet pkt = simple_packet::type_id::create ("pkt");
        pkt.randomize();
          ap.write (tr);                     //3.调用订阅端(subscriber)的write()方法,将数据发送出去
    end
  endtask
endclass

class subscriber extends uvm_component;
   ... 
   uvm_analysis_imp #(transaction, subscriber) analysis_export;//定义分析imp端口
   ...
   virtual function void write (transaction  tr);     //4. 定义write()方法
      `uvm_info (get_full_name(), "Sub got transaction", UVM_MEDIUM)
   endfunction
endclass

img

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   componentA  compA;
   componentB  compB;
   sub         sub1; 
   sub         sub2; 
   sub         sub3; 
 
   function new (string name = "my_env", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      // Create an object of both components
      compA = componentA::type_id::create ("compA", this);
      compB = componentB::type_id::create ("compB", this);
      sub1 = sub::type_id::create ("sub1", this);
      sub2 = sub::type_id::create ("sub2", this);
      sub3 = sub::type_id::create ("sub3", this);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      compA.put_port.connect (compB.put_export);  
 
      compB.ap.connect (sub1.analysis_export);       //5.端口连接
      compB.ap.connect (sub2.analysis_export);
      compB.ap.connect (sub3.analysis_export);
   endfunction
endclass

在environment中通过分析端口将B与sub1、sub2、sub3连接:

1). 首先在componentB中定义分析port端口,并在run_phase阶段调用write()函数

  • uvm_analysis_port #(transaction)   ap;
  • ap.write (tr);

2)、 在分析端口的订阅端(subscriber) ,我们需要定义write()方法

3)、在environment中通过分析端口将B与sub1、sub2、sub3连接 ,如下:

  • compB.ap.connect (sub1.analysis_export);
  • compB.ap.connect (sub2.analysis_export);
  • compB.ap.connect (sub3.analysis_export);

参考:

Mr.翟的博客

UVM TLM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值