TLM port分类
- port
- export
- imp
UVM TLM ports and exports are also used to send transaction objects cross different levels of testbench hierarchy.
Ports shall be used to initiate and forward packets to the top layer of the hierarchy. Exports shall be used to accept and forward packets from the top layer to destination. Implementation ports shall be used to define the put method at the target. Shown below are a few examples that use ports, exports and implementation for components at different hierarchy levels.
PORT互连规则
参考<uvm_port_base.svh>
中的uvm_port_base
中的connect函数,三种port的互联规则如下:
port连接到port
规则:只能是子port连接到父port。
Connecting port-to-port: CHILD.port.connect(PARENT.port)
port连接到export/imp
规则:port与export/imp有共同的祖父,即sibling可以是亲兄妹或者堂兄妹或者表兄妹
Connecting port-to-export: SIBLING.port.connect(SIBLING.export)
Connecting port-to-imp: SIBLING.port.connect(SIBLING.imp)
export连接到export/imp
规则:只能是父连子
Connecting export-to-export: PARENT.export.connect(CHILD.export)
Connecting export-to-imp: PARENT.export.connect(CHILD.imp)
连接方向
如上面所示,只能单向连接,不能反过来连接。
port类型
//-----------------
// Group: Port Type
//-----------------
// Enum: uvm_port_type_e
//
// Specifies the type of port
//
// UVM_PORT - The port requires the interface that is its type
// parameter.
// UVM_EXPORT - The port provides the interface that is its type
// parameter via a connection to some other export or
// implementation.
// UVM_IMPLEMENTATION - The port provides the interface that is its type
// parameter, and it is bound to the component that
// implements the interface.
typedef enum
{
UVM_PORT ,
UVM_EXPORT ,
UVM_IMPLEMENTATION
} uvm_port_type_e;
常见的TLM port
uvm_analysis_port
该端口扩展子uvm_port_base
,其中write函数的实现如下:
class uvm_analysis_port # (type T = int)
extends uvm_port_base # (uvm_tlm_if_base #(T,T));
function new (string name, uvm_component parent);
super.new (name, parent, UVM_PORT, 0, UVM_UNBOUNDED_CONNECTIONS);
m_if_mask = `UVM_TLM_ANALYSIS_MASK;
endfunction
virtual function string get_type_name();
return "uvm_analysis_port";
endfunction
// Method: write
// Send specified value to all connected interface
// Modified by Verdi
function void write (input T t);
uvm_tlm_if_base # (T, T) tif;
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_BEGIN(t,"write");
`endif
for (int i = 0; i < this.size(); i++) begin
tif = this.get_if (i);
if ( tif == null )
uvm_report_fatal ("NTCONN", {"No uvm_tlm interface is connected to ", get_full_name(), " for executing write()"}, UVM_NONE);
tif.write (t);
end
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_END(t,"write");
`endif
endfunction
// End
endclass
这里的write()
函数会通过遍历(this.get_if()
)查找出与该analysis port
互联的所有的实现口。注意analysis port
连接时可以是一对多的关系。比如连接关系可能包括:
- port->port->export->export->imp
- port->export->imp
- port->imp
this.get_if()
通过递归查找的方式找到所有imp,然后调用该实现口的write函数。我们再去看imp的write函数。
uvm_analysis_imp
// Modified by Verdi
class uvm_analysis_imp #(type T=int, type IMP=int)
extends uvm_port_base #(uvm_tlm_if_base #(T,T));
`UVM_IMP_COMMON(`UVM_TLM_ANALYSIS_MASK,"uvm_analysis_imp",IMP)
function void write (input T t);
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_BEGIN(t,"write");
`endif
m_imp.write (t);
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_END(t,"write");
`endif
endfunction
endclass
// End
该参数化类的第二个参数是该imp
例化时所在的uvm_component
的component type
。比如该实现口例化在类aes_rm
中,那么IMP=aes_rm
。注意该write()
函数的实现在IMP中,即示例aes_rm
中。
uvm_analysis_export
//------------------------------------------------------------------------------
// Class: uvm_analysis_export
//
// Exports a lower-level <uvm_analysis_imp> to its parent.
//------------------------------------------------------------------------------
class uvm_analysis_export #(type T=int)
extends uvm_port_base #(uvm_tlm_if_base #(T,T));
// Function: new
// Instantiate the export.
function new (string name, uvm_component parent = null);
super.new (name, parent, UVM_EXPORT, 1, UVM_UNBOUNDED_CONNECTIONS);
m_if_mask = `UVM_TLM_ANALYSIS_MASK;
endfunction
virtual function string get_type_name();
return "uvm_analysis_export";
endfunction
// analysis port differs from other ports in that it broadcasts
// to all connected interfaces. Ports only send to the interface
// at the index specified in a call to set_if (0 by default).
// Modified by Verdi
function void write (input T t);
uvm_tlm_if_base #(T, T) tif;
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_BEGIN(t,"write");
`endif
for (int i = 0; i < this.size(); i++) begin
tif = this.get_if (i);
if (tif == null)
uvm_report_fatal ("NTCONN", {"No uvm_tlm interface is connected to ", get_full_name(), " for executing write()"}, UVM_NONE);
tif.write (t);
end
`ifndef UVM_VERDI_NO_PORT_RECORDING
`UVM_IF_METHOD_END(t,"write");
`endif
endfunction
// End
endclass
该write()
函数很类似uvm_analysis_port
的write()
函数,不同之处:
- port type不一样,一个是
UVM_PORT
,一个是UVM_EXPORT
二者的m_if_mask
都是UVM_TLM_ANALYSIS_MASK
,连接的port数量均不受限制(UVM_UNBOUNDED_CONNECTIONS
)
uvm_tlm_fifo_base
该类为TLM FIFO类的基类,扩展自uvm_component
,定义了两个端口put_export和get_peek_export用于存取transaction。
// Port: put_export
//
// The ~put_export~ provides both the blocking and non-blocking put interface
// methods to any attached port:
//
//| task put (input T t)
//| function bit can_put ()
//| function bit try_put (input T t)
//
// Any ~put~ port variant can connect and send transactions to the FIFO via this
// export, provided the transaction types match. See <uvm_tlm_if_base #(T1,T2)>
// for more information on each of the above interface methods.
uvm_put_imp #(T, this_type) put_export;
// Port: get_peek_export
//
// The ~get_peek_export~ provides all the blocking and non-blocking get and peek
// interface methods:
//
//| task get (output T t)
//| function bit can_get ()
//| function bit try_get (output T t)
//| task peek (output T t)
//| function bit can_peek ()
//| function bit try_peek (output T t)
//
// Any ~get~ or ~peek~ port variant can connect to and retrieve transactions from
// the FIFO via this export, provided the transaction types match. See
// <uvm_tlm_if_base #(T1,T2)> for more information on each of the above interface
// methods.
uvm_get_peek_imp #(T, this_type) get_peek_export;
该类的new()
函数实例化了所有的端口:
// Function: new
//
// The ~name~ and ~parent~ are the normal uvm_component constructor arguments.
// The ~parent~ should be ~null~ if the uvm_tlm_fifo is going to be used in a
// statically elaborated construct (e.g., a module). The ~size~ indicates the
// maximum size of the FIFO. A value of zero indicates no upper bound.
function new(string name, uvm_component parent = null);
super.new(name, parent);
put_export = new("put_export", this);
blocking_put_export = put_export;
nonblocking_put_export = put_export;
get_peek_export = new("get_peek_export", this);
blocking_get_peek_export = get_peek_export;
nonblocking_get_peek_export = get_peek_export;
blocking_get_export = get_peek_export;
nonblocking_get_export = get_peek_export;
get_export = get_peek_export;
blocking_peek_export = get_peek_export;
nonblocking_peek_export = get_peek_export;
peek_export = get_peek_export;
put_ap = new("put_ap", this);
get_ap = new("get_ap", this);
endfunction
uvm_tlm_fifo
该类扩展自uvm_tlm_fifo_base
,其详细功能描述如下:
//------------------------------------------------------------------------------
//
// Class: uvm_tlm_fifo#(T)
//
// This class provides storage of transactions between two independently running
// processes. Transactions are put into the FIFO via the ~put_export~.
// transactions are fetched from the FIFO in the order they arrived via the
// ~get_peek_export~. The ~put_export~ and ~get_peek_export~ are inherited from
// the <uvm_tlm_fifo_base #(T)> super class, and the interface methods provided by
// these exports are defined by the <uvm_tlm_if_base #(T1,T2)> class.
//
//------------------------------------------------------------------------------
以上描述中规定了该FIFO的输入输出端口的连接,即:
- 通过
put_export
压入transaction - 通过
get_peek_export
获取transaction
该类中定义了一个mailbox,用于存储transaction。
local mailbox #( T ) m;
邮箱中存取transaction
的实现如下:
virtual task put( input T t );
m.put( t );
put_ap.write( t );
endtask
virtual task get( output T t );
m_pending_blocked_gets++;
m.get( t );
m_pending_blocked_gets--;
get_ap.write( t );
endtask
调用的port是put_ap
和get_ap
。
uvm_tlm_analysis_fifo
该类扩展自uvm_tlm_fifo
,内部定义了一个新的export,如下:
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);
// Port: analysis_export #(T)
//
// The analysis_export provides the write method to all connected analysis
// ports and parent exports:
//
//| function void write (T t)
//
// Access via ports bound to this export is the normal mechanism for writing
// to an analysis FIFO.
// See write method of <uvm_tlm_if_base #(T1,T2)> for more information.
uvm_analysis_imp #(T, uvm_tlm_analysis_fifo #(T)) analysis_export;
// Function: new
//
// This is the standard uvm_component constructor. ~name~ is the local name
// of this component. The ~parent~ should be left unspecified when this
// component is instantiated in statically elaborated constructs and must be
// specified when this component is a child of another UVM component.
function new(string name , uvm_component parent = null);
super.new(name, parent, 0); // analysis fifo must be unbounded
analysis_export = new("analysis_export", this);
endfunction
const static string type_name = "uvm_tlm_analysis_fifo #(T)";
virtual function string get_type_name();
return type_name;
endfunction
function void write(input T t);
void'(this.try_put(t)); // unbounded => must succeed
endfunction
endclass
其中write()
函数中的try_put
()来自其父类uvm_tlm_fifo
,其实现如下:
virtual function bit try_put( input T t );
if( !m.try_put( t ) ) begin
return 0;
end
put_ap.write( t );
return 1;
endfunction
try_put()
除了将t压入邮箱m
外,还将其推入put_ap
(uvm_analysis_port
类型),其作用是如果有外部其他的port和put_ap
互联,则t同时传递到外部互联的端口去。try_get()
函数也是类似的实现:
virtual function bit try_get( output T t );
if( !m.try_get( t ) ) begin
return 0;
end
get_ap.write( t );
return 1;
endfunction
其中get_ap
的定义在uvm_tlm_fifo_base
中:
uvm_analysis_port #(T) get_ap;
应用实例
sequencer和driver的互联
driver中通过port seq_item_port
获取sequencer发出的transaction,其定义如下:
// Port: seq_item_port
//
// Derived driver classes should use this port to request items from the
// sequencer. They may also use it to send responses back.
uvm_seq_item_pull_port #(REQ, RSP) seq_item_port;
与其互联的sequencer中的port为seq_item_export
,其定义如下:
// Variable: seq_item_export
//
// This export provides access to this sequencer's implementation of the
// sequencer interface.
//
uvm_seq_item_pull_imp #(REQ, RSP, this_type) seq_item_export;
在顶层agent中的互联如下:
drv.seq_item_port.connect(sqr.seq_item_export);
monior中的port
定义:
uvm_analysis_port #(my_transaction) analysis_port; //TLM analysis port
使用:
analysis_port.write(tr);
rm中的port
port定义:
uvm_blocking_get_peek_port#(my_in_transaction) in_port;
uvm_blocking_put_port#(my_out_transaction) out_port;
port使用:
in_port.get(in_tr);
out_port.put(exp_tr);
env中的port
env中例化了三组FIFO(也可以不例化):
uvm_tlm_analysis_fifo#(my_in_transaction) mon2rm_fifo;
uvm_tlm_analysis_fifo#(my_out_transaction) rm2chk_fifo;
uvm_tlm_analysis_fifo#(my_out_transaction) mon2chk_fifo;
互联关系如下:
iagt.mon.mon_analysis_port.connect(mon2rm_fifo.analysis_export);
rm.in_port.connect(mon2rm_fifo.blocking_get_peek_export);
rm.out_port.connect(rm2chk_fifo.blocking_put_export);
chk.rm_in_port.connect(rm2chk_fifo.blocking_get_export);
chk.mon_in_port.connect(mon2chk_fifo.blocking_get_export);
oagt.mon.mon_analysis_port.connect(mon2chk_fifo.analysis_export);
Note:根据uvm_tlm_fifo_base章节中new()
函数的定义:
blocking_get_peek_export = get_peek_export
blocking_get_export = get_peek_export
blocking_put_export = analysis_export
checker中的port
port定义:
uvm_blocking_get_port#(my_out_transaction) rm_in_port;
uvm_blocking_get_port#(my_out_transaction) mon_in_port;
port使用:
rm_in_port.get(tr);
mon_in_port.get(tr);
使用_decl宏
引用一段uvm class reference中原话:
The TLM implemenation declaration macros provide a way for components to provide multiple implemenation ports of the same implementation interface. When an implementation port is defined using the built-in set of imps, there must be exactly one implementation of the interface.
The important thing to note is that each `uvm_imp_decl creates a new class of type uvm_imp, where suffix is the input argument to the macro. For this reason, you will typically want to put these macros in a seperate package to avoid collisions and to allow sharing of the definitions.
Summary
TLM Implementation Port Declaration Macros
The TLM implemenation declaration macros provide a way for components to provide multiple implemenation ports of the same implementation interface.
Macros
`uvm_blocking_put_imp_decl
`uvm_nonblocking_put_imp_decl
`uvm_put_imp_decl
`uvm_blocking_get_imp_decl
`uvm_nonblocking_get_imp_decl
`uvm_get_imp_decl
`uvm_blocking_peek_imp_decl
`uvm_nonblocking_peek_imp_decl
`uvm_peek_imp_decl
`uvm_blocking_get_peek_imp_decl
`uvm_nonblocking_get_peek_imp_decl
`uvm_get_peek_imp_decl
`uvm_blocking_master_imp_decl
`uvm_nonblocking_master_imp_decl
`uvm_master_imp_decl
`uvm_blocking_slave_imp_decl
`uvm_nonblocking_slave_imp_decl
`uvm_slave_imp_decl
`uvm_blocking_transport_imp_decl
`uvm_nonblocking_transport_imp_decl
`uvm_transport_imp_decl
`uvm_analysis_imp_decl
以uvm_analysis_imp_decl
为例,示例代码如下:
`uvm_analysis_imp_decl(_ingress)
`uvm_analysis_imp_decl(_egress)
class myscoreboard extends uvm_component;
uvm_analysis_imp_ingress#(mydata, myscoreboard) ingress;
uvm_analysis_imp_egress#(mydata, myscoreboard) egress;
mydata ingress_list[$];
...
function new(string name, uvm_component parent);
super.new(name,parent);
ingress = new("ingress", this);
egress = new("egress", this);
endfunction
function void write_ingress(mydata t);
ingress_list.push_back(t);
endfunction
function void write_egress(mydata t);
find_match_in_ingress_list(t);
endfunction
function void find_match_in_ingress_list(mydata t);
//implement scoreboarding for this particular dut
...
endfunction
endclass
参考资料1: https://www.chipverify.com/uvm/tlm-preface
参考资料2:https://www.accellera.org/downloads/standards/uvm