UVM中的TLM1.0通信 | UVM笔记 2

Posted by Kion on January 28, 2020

TLM1.0

TLM是Transaction Level Modeling的缩写,表示事务级建模。transaction指的是把具有某一特定功能的一组信息封装在一起而成为的一个类。

TLM通信中常见的操作有put,get以及transport。put表示发送一个transaction,get表示索取一个transaction,transport表示发送并索取。动作发起者的端口均是PORT,接收者的端口均是EXPORT。

需要注意的是,这里put和get是两种过程,而不是一个过程的两个步骤。比如A要向Bput一个transaction,B的端口也应该是put前缀。put指A发送给B;get指A向B索取一个;transport是A发送给B后又向B索取。

uvm_blocking_put_port#(T);
uvm_nonblocking_put_port#(T);
uvm_put_port#(T);
uvm_blocking_put_export#(T);
uvm_nonblocking_put_export#(T);
uvm_put_export#(T);
...

UVM中各种端口的互连

要实现put/get/transport操作,只有PORT/EXPORT端口是不够的,还需要一个IMP端口。PORT/EXPORT端口只是起到传输的作用,无法进行其他操作,需要IMP端口进行进一步处理。IMP其实是implement的缩写,所以端口的最终执行操作要落实到IMP端口。

/*现在要完成A,B端口之间的put通信,应当有以下几步*/
/*首先,在A类中定义并实例化port,并发送tr*/
uvm_blocking_put_port#(my_transaction) A_port;
A_port = new("A_port",this);
A_port.put(tr)
/*然后,在B类中定义并实例化export,以及imp*/
uvm_blocking_put_export#(my_transaction) B_export;
uvm_blocking_put_imp#(my_transaction,B) B_imp;
B_export = new("B_export",this);
B_imp = new("B_imp",this);
/*连接B类中的export以及imp*/
B_export.connect(B_imp);
/*在B类中声明put函数或者任务*/
function void B::put(my_transaction tr);
endfunction
/*在env中连接A、B类端口*/
A_inst.A_port.connect(B_inst.B_export);
/*所以A中put的目的其实就是调用B中的put*/

PORT、EXPORT以及IMP的控制流优先级就是按该顺序。PORT可以直接与IMP连接。也可以只有EXPORT与IMP连接。对于一个在A中的C类实例,则A与C的PORT及EXPORT端口也可以连接,C与外部的传输,也通常要经过A的端口。blocking_get端口的使用与上述类似,同样要注意IMP端口应为终点。

对于blocking_transport端口,由于是双向的通信,所以B中的transport任务或函数应当要返回参数给A。

对于nonblocking端口,由于所有操作都是非阻塞的,在送出transaction之前要调用can_put或try_put判断是否可以发送。

可以注意到,port可以直连imp,那么为何还需要export呢?export在涉及到跨组件通讯时是有用的。比如一个scoreboard的port连接到agent的export,agent中的export连接到monitor的imp中。利用export连接组件及其容器,外部端口只要连接到容器即可,无需关心内部端口连接情况。

UVM中的通信方式

除了PROT/EXPORT/IMP端口,还有两种特殊端口:analysis_port与analysis_export。这两种端口可以连接多个IMP,且没有阻塞与非阻塞之分;并且只有一种操作:write。

uvm_analysis_port#(my_transaction) A_ap;
uvm_analysis_imp#(my_transaction,B) B_imp;
uvm_analysis_imp#(my_transaction,C) C_imp;
A_inst.A_ap.connect(B_inst.B_imp);
A_inst.A_ap.connect(C_inst.C_imp);

对于不在同一层次的component,端口之间的连接有三个方法。以monitor与scoreboard连接为例,monitor在agent中,agent与scoreboard同层次。

/*第一种,在env类中进行连接*/
o_agt.mon.ap.connect(scb.scb_imp);
/*第二种,在agent中先将monitor与agent的port相连,然后agent再与scoreboard相连*/
mon.ap.connect(o_agt.ap);
o_agt.ap.connect(scb_imp);
/*第三种,在agent定义一个port句柄指向monitor的port,再进行连接*/
ap = mon.ap;
o_agt.ap.connect(scb_imp);

当一个component中要接收多路数据时,需要注意区分write函数。

/*定义宏,使得生成两个新的IMP类,用于生成特定的IMP端口*/
`uvm_analyis_imp_decl(_monitor)
`uvm_analyis_imp_decl(_model)
uvm_analysis_imp_monitor#(my_transation,my_scoreboard) monitor_imp;
uvm_analysis_imp_model#(my_transation,my_scoreboard) model_imp;
/*编写write函数时,要加上与宏相对应的后缀*/
extern function void write_monitor(my_transaction tr);
extern function void write_model(my_transcation tr);

对于多路数据的通信,也可以使用FIFO。FIFO本质上是一块缓存加上两个IMP端口。FIFO有众多端口,可根据put/get/peek,与component中的各种端口相连。但为了方便使用,FIFO的IMP端口是不外显的,所以使用时收发的端口都是定义成port。

1lj8Fe.png

1ljvTO.png