UVM TLM

目录

 

TLM

TLM Put 

TLM Get 

 TLM Fifo [uvm_tlm_fifo]

TLM Hierarchy

TLM Analysis Port 

TLM Sockets

Using _decl macro in TLM 


TLM

TLM(transaction level modeling) 用于建立各组件和系统之间的抽象传输模型。在这个模型中,数据是以传输类(transaction,这个类中包含了各种随机变量)的形式流通于各个组件之间,各个组件之间的接口称为TLM端口

UVM提供了一系列的TLM端口用来各个组件之间通信,这样就提高了各个组件的可重用性和灵活性。

如下面的transaction类simple_packet:

class simple_packet extends uvm_object;
  `uvm_object_utils (simple_packet)
 
  rand bit [7:0] addr;
  rand bit [7:0] data;
     bit     rwb;
 
  constraint c_addr { addr > 8'h2a; };
  constraint c_data { data inside {[8'h14:8'he9]};
 
endclass

它可以通过TLM端口流通于各个组件之间,如下面的A和B:

 

发送请求(request)的端口为port,接收请求的端口为export.

TLM Put 

下面我们来看两个组件是如何通过port和export进行通信的。

如果我们的组件是通过继承uvm_component而来的,那么端口就已经被包括在这个组件中了。端口像其他的类一样,都需要声明和创建。声明是在组件中进行的,创建是在组件的build_phase()中使用new()来进行的。如下:

class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   // We are creating a blocking TLM put port which will accept a "simple_packet" type of data
   uvm_blocking_put_port #(simple_packet) put_port;//声明端口
   simple_packet  pkt;
 
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      // Remember that TLM put_port is a class object and it will have to be 
      // created with new ()
      put_port = new ("put_port", this);//创建端口
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      // Let us generate 5 packets and send it via the TLM put port
      repeat (5) begin
         pkt = simple_packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
         `uvm_info ("COMPA", "Packet sent to CompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
 
         // Call the TLM put() method of put_port class and pass packet as argument
         put_port.put (pkt);//调用put()发送数据
      end
   endtask
endclass

 可以看到,发送数据只需要用"端口名.put(数据包名)"的格式就可以了;在componentB中,我们需要描述put()方法,并且用uvm_blocking_put_imp定义一个export。如下:

class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Mention type of transaction, and type of class that implements the put ()
   uvm_blocking_put_imp #(simple_packet, componentB) put_export;

   function new (string name = "componentB", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      put_export = new ("put_export", this);
   endfunction
 
   task put (simple_packet pkt);//定义put()方法
      // Here, we have received the packet from componentA 
      `uvm_info ("COMPB", "Packet received from CompA", UVM_LOW)
      pkt.print ();
   endtask
endclass

然后我们需要在environment的connect_phase中连接conponentA和componentB,如下: 

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   componentA compA;
   componentB compB;
 
   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);
   endfunction
 
   // Connection between componentA and componentB is done here
   virtual function void connect_phase (uvm_phase phase);
      compA.put_port.connect (compB.put_export);  
   endfunction
endclass

TLM Get 

如果componentB向componentA要求数据时,我们可以用get()方法。此时B中的端口为port,A中的端口为export.

首先需要在组件B中用uvm_blocking_get_port定义一个port,并在build_phase中创建端口对象:

class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Create a get_port to request for data from componentA
   uvm_blocking_get_port #(simple_packet) get_port;
 
   function new (string name = "componentB", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      get_port = new ("put_export", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      simple_packet pkt;
      repeat (5) begin
         get_port.get (pkt);//调用get()方法
         `uvm_info ("COMPB", "ComponentA just gave me the packet", UVM_LOW)
         pkt.print ();
      end
   endtask
endclass

在componentA中,我们需要描述get()方法,并且用uvm_blocking_get_imp定义一个export。如下:

class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   // Create an export to send data to componentB
   uvm_blocking_get_imp #(simple_packet, componentA) get_export;
   simple_packet  pkt;
 
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      // Remember that put_port is a class object and it will have to be 
      // created with new ()
      get_export = new ("put_port", this);
   endfunction
 
   // This task will output a new packet 
   virtual task get (output simple_packet pkt);//定义get()方法
      // Create a new packet
      pkt = new();
      assert (pkt.randomize());
      `uvm_info ("COMPA", "ComponentB has requested for a packet, give the following packet to componentB", UVM_LOW)
      pkt.print (uvm_default_line_printer);
   endtask
endclass

 请注意,get()方法是输出,并且是得到该数据包的一端调用的get()方法。

在组件B中将两个端口连接,如下:

virtual function void connect_phase (uvm_phase phase);
   compB.get_port.connect (compA.get_export);  
endfunction

 TLM Fifo [uvm_tlm_fifo]

在前面的例子中,我们通过port和export实现了将数据从A发送到B,也可以在A和B之间加入一个FIFO进行缓存,如下图:

 

上面的例子如下:

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   componentA compA;
   componentB compB;
 
   // Create the UVM TLM Fifo that can accept simple_packet
   uvm_tlm_fifo #(simple_packet)    tlm_fifo;//定义TLM FIFO
 
   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);
 
      // Create a FIFO with depth 2
      tlm_fifo = new ("uvm_tlm_fifo", this, 2);
   endfunction
 
   // Connect the ports to the export of FIFO.//连接A和B到FIFO
   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
 
   // Display a message when the FIFO is full
   virtual task run_phase (uvm_phase phase);
      forever begin
         #10 if (tlm_fifo.is_full ()) 
               `uvm_info ("UVM_TLM_FIFO", "Fifo is now FULL !", UVM_MEDIUM)
      end
   endtask
endclass

TLM Hierarchy

我们来考虑下图中的情况:

 

 componentA中包含两个subcomp:subComp1和subComp2,两者之间通过TLM fifo连接;三部分的定义与前面相同,除了subComp2需要额外定义一个port以连接到componentA.这需要用到uvm_put_port,如下:

class subComponent2 extends uvm_component;
   `uvm_component_utils (subComponent2)
 
   // Mention type of transaction, and type of class that implements the put ()
   uvm_blocking_get_port #(simple_packet) get_port;
   uvm_put_port #(simple_packet)          put_portA;
 
   function new (string name = "subComponent2", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      get_port = new ("get_port", this);
      put_portA = new ("put_portA", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      simple_packet pkt;
      phase.raise_objection (this);
      repeat (5) begin
         #10;
         get_port.get (pkt);
         `uvm_info ("SCOMP2", "subComponent1 just gave me the packet", UVM_LOW)
         pkt.print ();
 
         // Remember to send this packet through the port, else ComponentB will not get data
         `uvm_info ("SCOMP2", "Send this to subComponent3", UVM_LOW)
         put_portA.put (pkt);
      end
      phase.drop_objection (this);
   endtask
endclass

在compnentA的connect_phase()中将 subComponent2的put_portA连接到compnentA的put_portA,如下:

class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   subComponent1 subComp1;
   subComponent2 subComp2;
   uvm_tlm_fifo #(simple_packet)    tlm_fifo;
   uvm_put_port #(simple_packet)  put_portA;
 
   function new (string name = "componentA", 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
      subComp1 = subComponent1::type_id::create ("subComp1", this);
      subComp2 = subComponent2::type_id::create ("subComp2", this);
 
      // Create a FIFO with depth 2
      tlm_fifo = new ("tlm_fifo", this, 2);
 
      // Create a port to connect with subcomponent2
      put_portA = new ("put_portA", this);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      subComp1.put_port.connect (tlm_fifo.put_export);
      subComp2.get_port.connect (tlm_fifo.get_export);
 
      // Connect put_portA of componentA with subComponent2
      subComp2.put_portA.connect (this.put_portA);//componentA连接subComponent2
   endfunction
endclass

在componentB中,我们需要定义一个export连接到FIFO的export,这需要用到uvm_put_export,如下:

class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   subComponent3                    subComp3;
   uvm_tlm_fifo #(simple_packet)    tlm_fifo;
   uvm_put_export #(simple_packet)  put_export;
 
   function new (string name = "componentB", 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
      subComp3 = subComponent3::type_id::create ("subComp3", this);
 
      // Create a FIFO with depth 2
      tlm_fifo = new ("tlm_fifo", this, 2);
 
      // Create the export to connect with subComponent
      put_export = new ("put_export", this);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      // Connect from componentB export to FIFO export
      put_export.connect (tlm_fifo.put_export);//componentB连接到fifo
 
      // Connect from FIFO export to subComponent3 port 
      subComp3.get_port.connect (tlm_fifo.get_export);
   endfunction
endclass

在environment中将A的port与B的export连接:

virtual function void connect_phase (uvm_phase phase);
   compA.put_portA.connect (compB.put_export);
 endfunction

TLM Analysis Port 

前面我们讨论了通过put/get方法来进行组件间的通信,这要求在export一端定义这两个方法,也就是说必须要有export端。UVM提供了一种分析端口(analysis port),它在组件中是以广播(broadcast)的形式向外发送数据的,而不管存在几个export或者没有export.

分析端口的定义用到关键字uvm_analysis_port,如下例:

class componentB extends uvm_component;
  `uvm_component_utils (componentB)
 
  // Create an analysis port by the name "ap" that can broadcast packets of type "simple_packet"
  uvm_analysis_port #(simple_packet) ap;//定义分析端口
 
  function new (string name = "componentB", uvm_component parent = null);
    super.new (name, parent);
  endfunction
 
  virtual function void build_phase (uvm_phase phase);
    super.build_phase (phase);
    ap = new ("analysis_port", this);//创建端口对象
  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();
           // Now pass it to other components via the analysis port write() method
          ap.write (pkt);//调用write()方法,将数据发送出去
    end
  endtask
endclass

在分析端口的订阅端(subscriber) ,我们需要定义write()方法;订阅端通过继承uvm_subscriber得到:

virtual class uvm_subscriber #(type T=int) extends uvm_component;
  typedef uvm_subscriber #(T) this_type;
 
  uvm_analysis_imp #(T, this_type) analysis_export;
 
  pure virtual function void write (T t);
endclass

 可以看到在uvm_subscriber中已经定义了analysis_export,所以在其子类中直接使用即可,如下:

class sub #(type T = simple_packet) extends uvm_subscriber #(T);
   `uvm_component_utils (sub)
 
   function new (string name = "sub", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
   endfunction
 
   // Note that the class object name has to be "t" - anything else will result
   // in compilation error
   virtual function void write (T t);
      `uvm_info (get_full_name(), "Sub got transaction", UVM_MEDIUM)
   endfunction
endclass
 
 

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

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);  
 
      // Connect Analysis Ports
      compB.ap.connect (sub1.analysis_export);
      compB.ap.connect (sub2.analysis_export);
      compB.ap.connect (sub3.analysis_export);
   endfunction
endclass

TLM Sockets

TLM2.0引入了socket的概念用来实现在发起端(initiator)目的端(target)异步双向数据传输.目的端的socket只能连接发起端的socket,反之也一样。

来看下面的例子,定义发起端socket:

class initiator extends uvm_component;
   `uvm_component_utils (initiator)
 
   // Declare a blocking transport socket (using initiator socket class)
   uvm_tlm_b_initiator_socket #(simple_packet) initSocket;//定义发起端socket
   uvm_tlm_time   delay;//定义发送延时
   simple_packet  pkt;
 
   function new (string name = "initiator", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
 
      // Create an instance of the socket
      initSocket = new ("initSocket", this);
      delay = new ();
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      // Let us generate 5 packets and send it via socket
      repeat (5) begin
         pkt = simple_packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
         `uvm_info ("INIT", "Packet sent to target", UVM_LOW)
         pkt.print (uvm_default_line_printer);
 
         // Use the socket to send data
         initSocket.b_transport (pkt, delay);//调用b_transport()方法,该方法允许时钟延时
      end
   endtask
endclass

在目的端除了要定义目的端socket外,还需要定义b_transport()方法,如下:

class target extends uvm_component;
   `uvm_component_utils (target)
 
   // Declare a blocking target socket
   uvm_tlm_b_target_socket #(target, simple_packet) targetSocket;
 
   function new (string name = "target", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
 
      // Create an instance of the target socket
      targetSocket = new ("targetSocket", this);
   endfunction
 
  // Provide the implementation method of b_transport in the target class
   task b_transport (simple_packet pkt, uvm_tlm_time delay);
      `uvm_info ("TGT", "Packet received from Initiator", UVM_MEDIUM)
      pkt.print (uvm_default_line_printer);
   endtask
endclass

在environment中将发起端和目的端连接:

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   initiator   init;
   target      tgt;
 
   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
      init = initiator::type_id::create ("init", this);
      tgt = target::type_id::create ("tgt", this);
   endfunction
 
   // Connect both sockets in the connect_phase
   virtual function void connect_phase (uvm_phase phase);
      init.initSocket.connect (tgt.targetSocket);  
   endfunction
endclass

Using _decl macro in TLM 

我们前面看到过将A中的数据发送到B中是通过port和export,而且用到了在B中定义的put()方法。假设现在存在这样一个情形,A和C都发送数据到B,并且都用到了B中的put()方法,那我们就需要区分开到底是哪个put()发送的数据,也就是说我们需要两个不同的put()方法分别对应A和C。如下图:

UVM为我们提供了`uvm_put_imp_decl ( )宏来解决上面的问题,如下:

`uvm_put_imp_decl (_1)
`uvm_put_imp_decl (_2)
 
class my_put_imp #(type T=int) extends uvm_component;
  uvm_put_imp_1 #(T, my_put_imp #(T)) put_imp1;
  uvm_put_imp_2 #(T, my_put_imp #(T)) put_imp2;
 
  function void put_1 (input T t);
    // puts coming from put_imp1
  endfunction
 
  function void put_2 (input T t);
    // puts coming from put_imp2
  endfunction
  ...
endclass

`uvm_put_imp_decl ( )宏必须在类的外部定义; 

调用上面的宏后,就创建了两个类uvm_put_imp_1和uvm_put_imp_2,于是上面的情况就变为下图:

我们来看一个例子,首先定义A和C:

//------------------------ componentA -------------------------------------
 
  class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   // We are creating a put_port parameterized to use a "simple_packet" type of data
   uvm_blocking_put_port #(simple_packet) put_port;
   simple_packet  pkt;
 
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      // Remember that put_port is a class object and it will have to be 
      // created with new ()
      put_port = new ("put_port", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      // Let us generate 5 packets and send it via the put_port
      repeat (2) begin
         pkt = simple_packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
         `uvm_info ("COMPA", "Packet sent to CompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
         put_port.put (pkt);//调用put
      end
   endtask
endclass
 
//------------------------ componentC -------------------------------------
class componentC extends uvm_component;
   `uvm_component_utils (componentC)
 
   // We are creating a put_port which will accept a "simple_packet" type of data
   uvm_blocking_put_port #(simple_packet) put_port;
   simple_packet  pkt;
 
   function new (string name = "componentC", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      // Remember that put_port is a class object and it will have to be 
      // created with new ()
      put_port = new ("put_port", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      // Let us generate 5 packets and send it via the put_port
      repeat (2) begin
         pkt = simple_packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
         `uvm_info ("COMPC", "Packet sent to CompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
         put_port.put (pkt);//调用put
      end
   endtask
endclass

然后在声明B之前调用宏:

`uvm_blocking_put_imp_decl (_1)//定义宏,宏的参数要与put_的后缀一致
`uvm_blocking_put_imp_decl (_2)
 
class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Mention type of transaction, and type of class that implements the put ()
   uvm_blocking_put_imp_1 #(simple_packet, componentB) put_imp1;//这相当于定义了两个端口
   uvm_blocking_put_imp_2 #(simple_packet, componentB) put_imp2;
 
   function new (string name = "componentB", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      put_imp1 = new ("put_imp1", this);//创建端口对象
      put_imp2 = new ("put_imp2", this);
   endfunction
 
   task put_1 (simple_packet pkt);
      `uvm_info ("COMPB", "Packet received from put_1", UVM_LOW)
      pkt.print (uvm_default_line_printer);
   endtask
 
   task put_2 (simple_packet pkt);//注意方法名与宏的参数要一致
      `uvm_info ("COMPB", "Packet received from put_2", UVM_LOW)
      pkt.print (uvm_default_line_printer);
   endtask
endclass

 在environment中将B和A、C分别连接:

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   componentA compA;
   componentC compC;
   componentB compB;
 
   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);
      compA = componentA::type_id::create ("compA", this);
      compC = componentC::type_id::create ("compC", this);
      compB = componentB::type_id::create ("compB", this);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      compA.put_port.connect (compB.put_imp1);  
      compC.put_port.connect (compB.put_imp2);
   endfunction
endclass

猜你喜欢

转载自blog.csdn.net/bleauchat/article/details/90757417