目录
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