文章目录
1.TLM通信概述
TLM是一种基于事务(transaction)的通信方式,通常在高抽象级语言例如SystemC或SV/UVM作为模块之间的通讯方式。
TLM通信保证了相邻组件之间的通信不再通过显示句柄引用,而是独立于组件的通信方式,为验证组件的复用提供了很好的封闭性。
-
TLM通信需要两个通信对象,这两个对象分别称为initiator和target。 initiator发起通信请求,target作为发起通信的响应方
-
通信发起方并不代表了transaction的流向起点,即不一定数据是从initiator流向target,也可能是从target流向了initiator
-
按照transaction的流向,将对象分为producer和consumer。producer数据产生方,consumer数据流向地
-
有了两个参与通信的对象之后,用户需要将TLM通信方法在target一端中实现,以便于initiator作为发起方可以调用target的通信方法,实现数据传输
-
在target实现了必要的通信方法之后,最后一步我们需要将两个对象进行连接,这需要在两个对象中创建TLM端口,继而在更高层次将这两个对象进行连接
TLM通信步骤: -
分辨出initiator和target, producer和consumer
-
在target中实现TLM通信方法
-
在两个对象中创建TLM端口,initiator中创建port端口,target端创建imp端口
-
在更高层次对两个对象的端口进行连接
分类:
从数据流向来看,传输方向分为单向和双向 -
单向传输:由initiator发起request transaction
-
双向传输:由initiator发起request transaction,传送至target,继而在target在消化了request transaction后,会发起responde transaction返回给initiator
三种端口类型: -
port:经常作为initiator的发起端,initiator凭借port才可以访问target的TLM通信方法
-
export:作为initiator和target中间层次的端口
-
imp:只能作为target接受request的末端,它无法作为中间层次的端口,所以imp到的连接无法再次延伸
2.端口的使用
-
声明port和export作为request发送方,参数只需要指定transaction类型参数
-
声明imp作为request接收方,参数有2个,第一个指定transaction类型,第二个指定它所在的component类型
关于TLM端口的类型、层次和对应的连接,可以从对应的连接关系中初步得出TLM端口连接的一般做法: -
在initiator端例化port,在中间层例化export,在target端例化imp,在build_phase中创建
-
多个port可以连接到同一个export或者imp,但单个port或者export无法连接到多个imp上
-
port可以连接port、export和imp;export可以连接export和imp上;imp是数据传输的终点,无法扩展连接
3.单向通信
单向通信指的是从initiator到target之间的数据流向是单一方向的,或者说initiator和target只能扮演producer和consumer中的一个角色。
- 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
实际上记忆这么多的端口名是有技巧的。按照每一个端口名的命名规则,它们指出通信的两个要素: - 是否阻塞的方式
- 何种通信方法,包括put、get、peek、get_peek,这些都需要在target中实现
数据blocking阻塞传输的方法分别包含:
- put:initiator通过该方法可以自己生成数据Tt,同时将该数据传送至target
- get:initiator通过该方法可以从target获取数据Tt,而target中的该数据则应消耗
- peek:initiator通过该方法可以从target获取数据Tt,而target中该数据应该保留
与上述三种task对应的nonblocking非阻塞的方法分别是: - try_put() :返回值默认为1或0
- can_put() :回调方法,在调用try_xx()方法时自动调用
- try_get()
- can_get()
- try_peek()
- can_peek()
这六个函数与其对应的task的区别在于,它们必须立即返回,函数try_xx()可以发送和获取数据,如果成功,返回1,失败返回0,可以通过can_xx()先试探target是否可以接受数据,再通过try_xx()发送,提高数据发送的成功率。
4.双向通信
与单向通信相同的是,双向通信的两端也分为initiator和target,但是数据的流向是端对端双向的。
双向通信的两端同时扮演着producer和consumer的角色。
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端
对于master端口或者slave端口的实现方式,非常类似于之前介绍的单向通信方式,只是imp端口所在的组件需要实现的方法更多了。
5.多向通信
- 多向通信仍然是两个组件之间的通信,而不是多个组件之间的通信
- 多向通信是指,如果initiator与target之间的相同TLM端口数目超过一个情况,在target端的解决方法
- UVM通过端口宏声明方式来解决这一问题,它解决的核心问题在于让不同端口对应不同的任务名,这样就不会造成方法名的冲突
comp1存在两个uvm_block_put_port端口,而从comp2存在两个uvm_blocking_put_imp端口,同时还要定义两个put()方法,我们可以在端口例化时创建不同的端口名,但是问题在于comp2中要定义两个task put(),不同的端口之间要求在imp端口一侧实现专属方法,这就造成了方法命名冲突,即无法在comp2中定义两个同名put方法。
UVM通过端口宏声明来解决这一问题,解决该问题的核心在于让不同的端口对应不同命的任务,这样不会造成方法名的冲突。简单来说就是加上声明imp端口的宏,如下:
`uvm_blocking_put_imp_dec1(SFX)
`uvm_nonblocking_put_imp_dec1(SFX)
`uvm_put_imp_dec1(SFX)
//方法类型可以替换为其他,SFX为imp端口和方法的后缀名
多向通信总结:
- 用户只需要例化多个imp端口的组件中实现不同名称的方法,使其与对应imp类型名保持一致
- 而对于port端口一侧的组件,则不需要关心调用的方法名称,因为该方法名并不会发生改变
5.通信管道
TLM通信的实现方式,这些通信有一个共同的地方即都是端对端的,同时在target一端需要实现传输方法,如put()和get(),对于monitor、coverage、collection等组件在传输数据时,会存在一端到多端,如何解决?
下面介绍几个TLM组件和端口可以帮助用户免除这些烦恼:
- TLM FIFO
- analysis port
- analysis TLM FIFO
- request & reponse通信管道
5.1 TLM_FIFO
uvm_tlm_fifo类是一个新的组件,它继承与uvm_component,且预先内置了多个端口,实现了多个对应方法供用户使用,从组件内部来看,它内置了一个mailbox #(T),该信箱没有尺寸限制,用来存储数据类型T,uvm_tlm_fifo的多个端口对应的方法都是利用该信箱实现了数据读写。
5.2 Analysis Port
除了端对端的传输,在一些情况下还有多个组件会对同一个数据进行运算处理。
- 可以实现一端对多端的需求,即数据是从同一个源的TLM端口出发到达不同的组件
- 如果数据源端发生变化需要通知跟它关联的多个组件时,可以利用软件设计模式之一观察者模式来实现
observe pattern的核心在于: - 第一,这是一个initiator端到多个target端的方式
- 第二,analysis port采用的是”push"模式,即从initiator端调用多个target端的write()函数实现数据传输
5.3 Analysis TLM FIFO
- uvm_tlm_analysis_fifo为用户提供了可以搭配uvm_analysis_port端口、uvm_analysis_imp端口和write()函数
- uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo,这表明它本身具有面向单一TLM端口的数据缓存特性,而同时该类又有一个uvm_analysis_imp端口
连接方式:
- 将initiator的analysis port连接到uvm_tlm_analysis_fifo 的 get_export 端口,这样数据可以从initiator发起,写到各个uvm_tlm_analysis_fifo的缓存中
- 将多个target的get_port连接到tlm_analysis_fifo的get_export,注意保持端口类型的匹配,这样从target一侧需要调用get()方法就可以得到先前存储在tlm_analysis_fifo中的数据
5.4 request & response 通信管道
UVM提供了两种简单的通信管道,他们作为数据缓存区域,既有TLM端口从外侧接收request 和 response,同时也有TLM端口供外侧获取request和response。
- uvm_tlm_req_rsp_channel
- uvm_tlm_transport_channel