UVM通信篇之二:单向、双向及多向通信(上)

本文转自:http://www.eetop.cn/blog/html/28/1561828-5940099.html

上一节已经通过一个实例,帮助读者们了解建立TLM通信的几个步骤。首先,需要明确initiator和target的区分,明白哪个组件首先发起了request,其次需要再按照transaction的流动方向划分producer和consumer,然后按照前后两种划分的组合,就知道使用哪一种TLM端口类型。当然,在从initiator到target包括中间穿过的层次,都应该保持相同的TLM传输数据参数。在这一节中我们还将继续深入TLM的端口类型,通过将端口的通信划分为单向、双向以及多向通信类型带领读者们深入这些不同端口通信的应用场景和实现过程。

 

单向通信(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

  • uvm_nonblocking_peek_PORT

  • uvm_peek_PORT

  • uvm_blocking_get_peek_PORT

  • uvm_nonblocking_get_peek_PORT

  • uvm_get_peek_PORT

 

这里的PORT代表了三种端口名:port、export和imp。这么一计算的话,那么对于单一方向的传输端口一共有36种。看起来这么多的端口类型似乎对读者的记忆不太友好,实际上记忆这么多的端口名是有技巧的。按照每一个端口名的命名规则,它们也指出了通信的两个要素:

  • 是否是阻塞的方式(即可以等待延时)

  • 何种通信方法

 

如果将这两种组合做一个表格的话,那么这些端口对应的通信方式就很清楚了,读者们也可以根据需要选择端口类型:

 

 


首先,阻塞的传输方式通过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中的该数据则应消耗。

  • peek:initiator通过该方法可以从target获取数据T t,而target中的该数据还应该保留。

 

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

  • try_put

  • can_put

  • try_get

  • can_get

  • try_peek

  • can_peek

 

这六个函数与其对应的任务的区别在于,它们必须立即返回,如果try_xxx函数可以发送或者获取数据,那么函数还应该返回1,如果执行失败则应该返回0。或者通过can_xxx函数先试探target是否可以接收数据,通过返回值,再通过try_xxx函数发送,提高数据发送的成功率。

 

接下来我们给出一段例码,来看看如何通过上述的TLM类型端口来实现数据的传输。这个例子中,comp1为initiator,comp2为target。comp1会将数据写入(put)到comp2中,带comp2处理完之后,comp1会再从comp2中获取(get)处理过的数据。

 

 

 

输出结果:

 

 

 

首先在comp1中例化了两个TLM端口  

   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中的方法。在这里读者需要注意的是,在调用方法之前的几个步骤是必不可少的:

  • 定义端口

  • 实现对应方法

  • 在上层将端口进行连接

 

双向通信(bidirectional communication)

与单向通信相同的是,双向通信的两端也分为initiator和target,但是数据的流向在端对端之间是双向的。读者也可以认为双向通信中的两端同时扮演者producer和consumer的角色,而initiator作为request的发起方,在发起了request之后,还会等待response的返回。对于目前的双向端口类型,一共分为了下面这么多种:

  • 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端。

对于master端口或者slave端口的实现方式,非常类似于之前介绍的单向通信方式,只是imp端口所在的组件需要实现的方法更多了。我们接下来主要为读者们介绍transport双向通信方式,因为它很明显区分于之前的单向通信方式(即调用的方法传送数据是单向的)。

 

 

 

输出结果:

 

 

 

从上面的例码可以看出,类似于单向端口连接的是端口的例化和连接,不同的只是要求实现对应的双向传输任务

    task transport(itrans req, output otrans rsp)

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页