不同组件之间的通信,在SV中可以通过旗语、信箱及事件来实现。UVM提供了更方便的方法。TLM全称为transaction level modeling,事务级建模。相对DUV中各个信号线的连接,一个item(transaction)就是把具有特定的功能信息封装组成的一个类。
根据口的优先级可以分为port、export和imp(import),控制流的优先级排序从高到低。PORT、EXPORT、IMP体现的是控制流而不是数据流。无论是get还是put操作,其发起者拥有的都是高优先级的端口。作为一个EXPORT来说,只能被动地接收 PORT的命令。transport操作相当于一次put操作加一次get操作,这两次操作的“发起者”都是A,目标都是 B。A上的端口依然是PORT,而B上的端口依然是EXPORT。只有高优先级的端口才能向低优先级的端口发起三种操作。UVM中使用connect函数来建立连接关系。这种通信时的主次顺序也适用于连接时,只有发起者才能调用connect函数,而被动承担者则作为connect的参数。如A要和B通信(A是发起者),那么可以这么写:A.port.connect(B.export),但是不能写成B.export.connect(A.port)。因为在通信的过程中,A是发起者,B是被动承担者。
在UVM中,PORT恰如一道门,EXPORT也如此。既然是一道门,那么它们也就只是一个通行的作用,它不可能把一笔transaction存储下来, 除了转发操作之外不作其他操作。这笔transaction一定要由B_export后续的某个组件进行处理。在UVM中,完成这种后续处理的也是一种端口:IMP。如果没有IMP会报错:Connection Error] connection count of 0。
根据动作类型可分为put、get、peek、get_peek和transport。
put:通信的发起者将一个item发送给目标。
get:通信的发起者向目标获取一个item。
transport:通信的发起者发给目标一个item并接收目标的返回。相当于一个put+get。
peek:与get系列端口类似,用于主动获取数据,当get任务被调用时,FIFO内部缓存中会少一个transaction,而 peek被调用时,FIFO会把transaction复制一份发送出去,其内部缓存中的transaction数量并不会减少。
get_peek:集合了get操作和peek操作两者的功能。当动作发起者和动作接收者使用的端口是get_peek类型时,需要在动作接收者定义一个名字为get的任务/函数,一 个名字为peek的任务/函数。估摸两个可以二选一。
根据是否阻塞可分为:blocking、nonblocking和同时适用阻塞非阻塞的口。当A向B发送数据时,如果B没接收,A是等待还是立即返回。等待就是阻塞,立即返回就是非阻塞。
另外,在uvm中 还有analysis_port、analysis_export和analysis_imp。和上述的区别是,这些是广播口,即一个ap口(aep)可以连接多个imp口,因此不需等待与其相连的其他端口的响应,即不存在阻塞的概念。其动作也只有write一个动作,无put、try_put、can_put等。
PORT | put | get | peek |
blk | uvm_blocking_put_port | uvm_blocking_get_port | uvm_blocking_peek_port |
nonblk | uvm_nonblocking_put_port | uvm_nonblocking_get_port | uvm_nonblocking_peek_port |
uvm_put_port | uvm_get_port | uvm_peek_port | |
get_peek | transport | ||
blk | uvm_blocking_get_peek_port | uvm_blocking_transport_port | |
nonblk | uvm_nonblocking_get_peek_port | uvm_nonblocking_transport_port | |
uvm_get_peek_port | uvm_transport_port |
EXPORT | put | get | peek |
blk | uvm_blocking_put_export | uvm_blocking_get_export | uvm_blocking_peek_export |
nonblk | uvm_nonblocking_put_export | uvm_nonblocking_get_export | uvm_nonblocking_peek_export |
uvm_put_export | uvm_get_export | uvm_peek_export | |
get_peek | transport | ||
blk | uvm_blocking_get_peek_export | uvm_blocking_transport_export | |
nonblk | uvm_nonblocking_get_peek_export | uvm_nonblocking_transport_export | |
uvm_get_peek_export | uvm_transport_export |
IMP | put | get | peek |
blk | uvm_blocking_put_imp | uvm_blocking_get_imp | uvm_blocking_peek_inp |
nonblk | uvm_nonblocking_put_export | uvm_nonblocking_get_imp | uvm_nonblocking_peek_imp |
uvm_put_export | uvm_get_imp | uvm_peek_imp | |
get_peek | transport | ||
blk | uvm_blocking_get_peek_imp | uvm_blocking_transport_imp | |
nonblk | uvm_nonblocking_get_peek_imp | uvm_nonblocking_transport_imp | |
uvm_get_peek_imp | uvm_transport_imp |
uvm_analysis_port | |
uvm_analysis_export | |
uvm_analysis_imp |
在UVM中,只有 IMP才能作为连接关系的终点。如果是PORT或者EXPORT作为终点,则会报错。无论是不是广播口,其最终的数据归宿都要经过imp。不论是port→export→imp或者export→imp。前者的话在目标的component需要将export和imp相连,否则会报connection error错误。一个uvm_blocking_put_port的new函数的原型如下:
function new(string name,
uvm_component parent,
int min_size = 1;
int max_size = 1);
如果不看后两个参数,那么这个new函数其实就是一个uvm_component的new函数。new函数中的min_size和max_size指的是必须连接到这个PORT的下级端口数量的最小值和最大值,即这一个PORT应该调用的connect函数的最小值和最大值。如果采用默认值,即min_size=max_size=1,则只能连接一个EXPORT。 相应的一个export后边也得跟着一个imp。
高优先级的动作发起者在发起动作如put、get、write时,会调用动作接受者(imp所在的component)相应的函数或任务,因此应该在低优先级的动作接受者(imp所在的component)中定义相应名称的函数或任务。不实现将会报错。如使用ap口,mon和refm都传递给scb。则需要在mon和refm都使用write函数写进去ap.write(item)。同时需要在动作接受者scb定义相应的write任务,为此还增添了`uvm_analysis_imp_decl(_mon)和`uvm_analysis_imp_decl(_refm)来创建相应的函数和类。
本文章参考《UVM实战》、《芯片验证漫游指南》等资料整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。