IC验证学霸笔记4——UVM--单向通信、双向通信、多向通信

1 单向通信

单向通信 (unidirectional communication) 指的是从 initiator 到 target 的数据流向是单一方向的, 或者 initiator 和 target 只能扮演 producer 和 consumer 中的一个角色
在UVM中,单数据流向的 TLM 端口有很多类型(部分):
uvm_blocking_put_PORT 
uvm_nonblocking_put_PORT
uvm_put_PORT 
uvm_blocking_get_PORT 
uvm_nonblocking_get_PORT 
uvm_get_PORT 
uvm_blocking_peek_PORT 
uvrn_nonblocking_peek_PORT
uvm_peek_PORT 
uvrn_blocking_get_peek_PORT
uvm_nonblocking_get_peek_PORT
uvm_get_peek_PORT 
按照 UVM 端口名的命名规则, 它们指出了通信的两个要素:
•    是不是阻寒的方式(即可以等待延时);
•    何种通信方法。

单向通信--方法
•    阻塞传输方式将blocking前缀作为函数名的一部分, 而非阻塞方式则名为nonblocking。 阻塞端口的方法类型为task, 这保证了可以实现事件等待和延时;非阻塞端口的力式类型为function, 这确保了方法调用可以立即返回。
•    我们从方法名也可以发现, 例如uvm_blocking_put_PORT提供的方法 task put()会在数据传送完后返回,uvm_nonblocking_put_PORT对应的两个函数try_put()和can_put()是立刻返回的。
•    uvm_put_PORT则分别提供了blocking和nonblocking的方法, 这为通讯方式提供了更多选择。

blocking阻塞传输的方法包含:

•   Put (): initiator先生成数据T t, 同时将该数据传送至target。
•   Get(): initiator从target获取数据T t, 而target中的该数据T t则应消耗。
•   Peek(): initiator从target获取数据Tt, 而target中的该数据T t还应保留。

与上述三种任务对应的nonblocking非阻塞方法分别是:

•    try/can_put()
•    try/can_get()
•    try/can_peek()

•   这六个非阻塞函数与对应阻塞任务的区别在于,它们必须立即返回, 如果try_xxx函数可以发送或者获取数据, 那么函数应该返回1, 如果执行失败则应返回0。
•    或者通过can_xxx函数先试探target是否可以接收数据, 如果可以, 再通过try_xxx函数发送, 提高数据发送的成功率。
首先comp1例化了两个port端口:
•     uvm_blocking_put_port #(itrans) bp_port; 
•     uvm_nonblocking_get_port #(otrans) nbg_port; 

comp2作为target则相应例化了两个对应的imp端口:
•     uvm_blocking_put_imp #(itrans, comp2) bp_imp; 
•     uvm_nonblocking_get_imp #(otrans, comp2) nbg_imp; 

env1环境将comp1与comp2连接之前, 需要在comp2中实现两个端口对应的方法:
•     task put(itrans t) 
•     function bit try_get (output otrans t) 
•     function bit can_get();

接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。
因此在调用端口方法之前的几个步骤是必不可少的:
•    定义端门
•    实现对应方法
•    在上层将端口进行连接 

2 多向通信

与单向通信相同的是,双向通信(bidirectional communication)的两端也分为initiator和target,但是数据流向在端对端之间是双向的。
双向通信中的两端同时扮演着producer和consumer的角色, 而initiator作为request发起方, 在发起request之后, 还会等待response返回。

UVM双向端口分为以下类型:
•     uvm_blocking_transport_PORT
•     uvm_nonblocking_transport_PORT
•     uvm_transport_PORT
•     uvm_blocking_master_PORT 
•     uvm_nonblocking_master_PORT 
•     uvm_master_PORT 
•     uvm_blocking_slave_PORT 
•     uvm_nonblocking_slave_PORT 
•     uvm_slave_PORT 


双向端口按照通信握手方式可以分为: 
•    transport双向通信方式
•    master和slave双向通信方式

 transport端口通过transport()方法, 可以在同一方法调用过程中完成REQ 和RSP的发出和返回。
 master和slave的通信方式必须分别通过put、 get和peek的调用, 使用两个方法才可以完成一次握手通信。
master端口和slave端门的区别在于,当initiator作为master时, 它会发起REQ送至target端, 而后再从target端获取RSP; 当initiator使用slave端口时,它会先从target端获取REQ, 而后将RSP送至target端。

从上面代码可以看出,双向端口处理类似于单向端口的是端口例化和连接, 不同的只是要求实现对应的双向传输任务:
task transport (itrans req, output otrans rsp)

3 多向通信

多向通信(multi-directional communication)这个概念听起来容易让读者产生歧义, 因为这种方式服务的仍然是两个组件之间的通信, 而不是多个组件之间的通信, 毕竟多个组件的通信仍然可以由基础的两个组件的通信方式来构建。
多向通信指的是, 如果initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。
comp1有两个uvm_blocking_put_port, 而comp2有两个uvm_blocking_put_imp端口, 我们对于端口例化可以给不同名字, 连接也可以通过不同名字来索引, 但问题在于comp2中需要实现两 一
个task put (itrans t), 又因为不同端口之间要求在imp端口侧实现专属方法, 这就造成了方法命名冲突, 即无法在comp2中定义两个同名的put任务。
UVM通过端口宏声明方式来解决这一问题, 它解决问题的核心在于让不同端口对应不同名的任务, 这样便不会造成方法名的冲突。 UVM 为解决多向通信问题的宏按照端口名的命名方式分为:
当一个组件的两个端口通过相同方法(比如task put())向另一个组件传输数据时, 需要使用上述的宏, 分别声明两个不同的 imp 类型, 完整的实现步骤包括:
•    选择正确的 imp 宏来定义不同的 imp 端口类型, 而宏的参数 SFX (后缀名)也会转化为相应的 imp 端口类型名。
•    在 imp 例化的组件中, 分别实现不同的 put_SFX 方法。
•    在 port 例化的组件中,不需要对目标imp端口类型进行区分,例如compl 中的 bp_portl和 bp_port2 为相同的端口类型。
•    对于 comp 调用 put()方法, 它只需要选择 bp_port1 或 bp_port2, 而不需要更替 put()方法名, 即仍然按照 put()来调用而不是 put_pl()或 put_p2()。
•    在上层环境应该连接 comp1 和 comp2 的 TLM端口。
用户只需要在例化多个imp端口的组件中实现不同名称的方法,使其与对应imp类型名保持一致。而对于port端口一侧的组件,则不需关心调用的方法名称,因为该方法名并不会发生改变。所以通过这种方式可以防止通信方法名的冲突,从而解决多向通信的问题。

4 通信管道

TLM通信的实现方式, 这些通信有一个共同的地方即都是端对端的, 同时在target一端需要实现传输方法, 例如put()或者get()。
对于monitor、 coverage collector等组件在传输数据时, 会存在一端到多端的传输, 如何解决这一问题?
几个TLM组件和端口可以帮助用户免除这些烦恼:
在 一般TLM传输过程中, 无论是initiator给target发起一个 transaction, 还是initiator从target获取一 个transaction, transaction 最终都会流向consumer中(initiator和target都可以是consumer)。

TLM FIFO
consumer在没有分析transaction时, 我们希望将该对象先存储到本地FIFO中供稍后使用。

TLM FIFO uvm_tlm_fifo类是一个新组件, 它继承于uvm_component 类, 而且已经预先内置了多个端口以及实现了多个对应方法供用户使用。

一个 uvm_ tlm _ fifo 会包含多个 TLM 端口, 我们摘出常用端口:

uvm_tlm_fifo的功能类似于mailbox,不同的地方在于uvm_tlm_fifo提供了各种端口供用户使用。我们推荐在initiator端例化put_port或者get_peek_port, 来匹配uvm_tim_fifo的端口类型。如果用户例化了其它类型的端口, uvm_tlm_fifo还提供put、get以及peek对应的端口:

 Analysis Port

除了端对端的传输, 在一些情况下还有多个组件会对同一个数据进们运算处理。
如果这个数据是从同一个源的TLM端口发出到达不同组件, 这就要求该种端口可以满足从一端到多端的需求。
如果数据源端发生变化需要通知跟它关联的多个组件时, 我们可以利用软件的设计模式之一观察者模式(observer pattern)来实现。

observer pattern 的核心在于, 第一, 这是从一个 initiator 端到多个 target 端的方式;第二, analysis port 采取的是 "push" 模式, 即从 initiator 端调用多个 target 端的 write()函数实现数据传输。
一个典型的 analysis port 类型端口的连接方式, 类似于其他 TLM 端口的是, 按照传输方法和端口方向组合可以将 analysis port分为uvm_ analysis _port、uvm_analysis_ export 以及 uvm_analysis_imp。 target 一侧例化了 uvm_analysis_imp 后还需要实现 write()函数。 最后, 在顶层可以将 initiator 端的 uvm _ analysis _port 同多个 target 端的 uvm_analysis_ imp 进行连接。 在 initiator 端调用wirte()函数时, 实际上它是通过循环的方式将所有连接的 target 端内置的 write()函数进行了调用。由于函数立即返回的特点, 无论连接多少个 target 端, initiator 端调用 write()函数总是可以立即返回的。这里稍微不同于之前单一端口函数调用的是,即使没有target 与之相连,调用 write() 函数时也不会发生错误。

 Analysis TLM FIFO
由于 analysis 端口实现了一端到多端的 TLM 数据传输,一个新的数据缓存组件类uvm_tlm_analysis_fifo为用户提供了可以搭配 uvm_analysis_port 端口、 uvm_analysis_imp 端口 和write()函数. uvm_tlm_analysis_fifo 类继承于 uvm_tlm_fifo, 这表明它本身具有面向单一 TLM 端口的数据缓存特性, 同时该类又有一个 uvm_analysis_imp 端口 analysis_ export 且实现了 write()函数:

基于 initiator 到多个 target 的连接方式,用户如果想轻松实现一端到多端的数据传输,可以下图那样插入多个 uvm_ tlm _analysis_ fifo。这里给出连接方式:
•    将initiator的analysis port连接到tlm_analysis_fifo的get_export端口,这样数据可以从initiator发起, 写入到各个tlm_analysis_fifo的缓存中。
•    将多个target的get_port连接到tlm_analysis_fifo的get_export, 注意保持端口类型的匹配,这样从target一侧只需要调用get()方法就可以得到先前存储在 tlm_analysis_fifo中的数据。

 Request&Response 通信管道

双向通信端口 transport, 即通过在 target 端实现 transpo()方法可以在一次传输中既发送 request 又接收 response。
UVM提供了两种简便的通信管道, 它们作为数据缓存区域, 既有TLM端口从外侧接收request和response, 同时也有TLM端口供外侧获取request和response。 这两种TLM通估管道分别是:
•    uvm_tlm_req_rsp_channel
•    uvm_tlm_transport_channel
uvm_tlm_req_rsp_channel 提供的端口首先是单一方向的,为了让端口列表清爽一些,下面只列出该类的例化端口:
有了这么多丰富的端口, 用户可以在使用成对的端口进行数据的存储和访问。 需要注意
的是, uvm_tlm _req_rsp _ channel 内部例化了两个 mailbox 分别用来存储 request 和 response: 

protected uvm_tlm_fifo#(REQ) m_request_fifo;
protected uvm_tlm_fifo #(RSP) m_response_fifo; 
例如, initiator 端可以连接 channel 的 put_request_export, target 连接 channel的 get_peek_ request_ export, 同时 target 连接 channel 的 put_response_ export, initiator 连接 channel 的 get_peek _response_ export 端口。 如下图 所示, 这种对应使得 initiator 与 target 可以利用 uvrn_tlm_req__rsp_channel 进行 request与response 的数据交换。

通过这种对应的方式, 使得initiator与target可以利用uvm_tlm_req_rsp_channel进行request与response的数据交换。
或者, 也可以利用另一种连接方式,如下:
需要变化的是initiator 端到req_rsp_ channel 的连接, 应该修改为:
至于transport_channel和target之间的连接, 则可以仍然保留之前的单向传输连接方式。

注:优秀验证学员随堂笔记,已经征求到学生的同意,会持续给牛友们分享!
大家看完记得 一键三连!多多支持
#你的秋招进展怎么样了##深度学习##芯片IC验证工程师##你为什么选择硬件行业##做项目#
全部评论
每个点都说的很详细
点赞 回复 分享
发布于 2022-09-29 16:29 河南

相关推荐

08-11 11:29
门头沟学院 Java
点赞 评论 收藏
分享
评论
2
5
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务