UVM入门进阶5:TLM通信_单向双向多向_通信管道_TLM2.0

参考文档链接:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/

本节目录:

  • TLM通信

    • 单向通信
    • 双向通信
    • 多向通信
    • 通信管道
  • TLM2.0

双向通信(使用较少)与TLM2.0(与SystemC通信),都作为了解

1 TLM通信

transaction level module

1.1 概述

不用端口也是ok的(如实验2)但使用端口可以降低耦合度,有利于更长远的维护

1.1.1 系统原型与芯片验证

芯片开发流程中,两个地方对项目的助推起到关键作用:

  • 系统原型
  • 芯片验证

系统原型:

芯片验证:

实际上sv在组件间发送的数据都是trans,把硬件发送的数据抽象到一个数据包中,模拟硬件内部多个周期的数据传输

两者中的TLM与其作用:

TLM是一个标准,而不一种特定语言:

TLM引用越来越广泛,uvm与sv验证平台互通可以通过这个标准

1.1.2 开发结构图(软件开发消耗人力较多)

软件开发的两方面:

  • 对系统原型开发,没有硬件
  • 等到设计开发到达稳定阶段时在稳定系统(simulator,FPGA,emulator硬件加速器)基础上

1.2.1 TLM

TLM:

如何提高系统模型仿真性能:1、自身运算优化;2、通信优化

TLM就是通信角度的一种优化方式

1.2.2 TLM通信基本概念

  • 基于事务的通信方式,可用于多种语言的模型中
  • 需要两个通信对象,initiator与target,谁先发请求谁是initiator,谁响应谁是target
    • initiator,target并不代表数据传输方向
  • transaction数据流向分类分为producer(数据产生)与consumer(数据流向)
  • producer/consumer,initiator/target,关系不是固定的

1.2.3 TLM通信步骤:

  • 确定通信对象
  • 将TLM通信方法在target一段实现(从而init调用target方法)
  • 两个对象中创建TLM端口(uvm中端口预设,只需例化)
  • 在更高层次中将两个对象的端口进行连接

1.2.4 示意图

1.2.5 分类

单向(unidirection)和双向(bidirection)传输:

  • 单向传输:由initor发起req trans
  • 双向传输:由initor发起req trans,传送到target;target处理req trans后发起rsp trans,返回initor

端口类型三种:

  • prot:常作为initor发起端,initor借助port才能访问target的TLM通信方法(port也能连接到port上)
  • export:作为initor与target中间层次的端口

  • imp(implementation):作为target接收rsp的末端,无法作为中间层次端口,所以imp的连接无法再次延伸(无法再连接到imp或其他端口上)

单向通信补充(参考1.3节):

  • 多个port可以连到同一port/export/imp上
  • 一个port不能连接到多个imp
  • 为什么:
    • 多个initor可对同一组件发起req请求
    • 同一initor无法连接多个target

如上可知,将传输方向与端口类型组合,得出TLM端口共六类,进而理解TLM端口的继承树:

1
2
3
4
5
6
uvm_UNDIR_port #(trans_t)
uvm_UNDIR_export #(trans_t)
uvm_UNDIR_imp #(trans_t)
uvm_BIDIR_port #(trans_t, imp_parent_t)
uvm_BIDIR_export #(trans_t, imp_parent_t)
uvm_BIDIR_imp #(trans_t, imp_parent_t)

端口既不是obj也不是comp类型,比较特殊不能通过type_id::create创建,即不存在factory的注册与创建

1.3 端口的使用:

1.3.1 结构示意图,port,import,export的符号

1.3.2 上述结构图代码

注意端口类型:

port:uvm_blocking_get_port

1.3.3 示例得出的TLM通信常规步骤

2 单向通信

2.1 概述

2.1.1 单向通信(unidirection communication)

指的是从initiator到target之间的数据刘翔是单一方向的,也就是书initor与target只能扮演producer或consumer其中之一

UVM中单一数据流向的TLM端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//put
uvm_blockong_put_PORT
uvm_nonblockong_put_PORT
uvm_put_PORT
//get
uvm_blockong_get_PORT
uvm_nonblockong_get_PORT
uvm_get_PORT
//peek
uvm_blockong_peek_PORT
uvm_nonblockong_peek_PORT
uvm_peek_PORT
//get_peek
uvm_blockong_get_peek_PORT
uvm_nonblockong_get_peek_PORT
uvm_get_peek_PORT

2.1.2 类型与表(非常重要)

PORT代表三种端口名:port、export和imp,例如:

  • uvm_blocking_put_PORT
  • uvm_blocking_put_EXPORT
  • uvm_blocking_put_IMP

按照UVM端口命名规则,它们指出了通信的两个要素:

  • 是否是阻塞方式(block/nonblock)
  • 何种通信方法(put/get/peek/get_peek)

例如:

  • 看到uvm_blocking_put_PORT

    • 我们要在target实现task put(T t)这一个方法
  • 看到uvm_nonblocking_put_PORT

    • 我们要在target实现实现 function bit try_put(T t)function bit can_put()
  • 看到uvm_put_PORT
    • 我们要在target实现上述三个方法

关于put,get,peek,get_peek:

  • put:initiator—>target
  • get:target—>initiator
  • peek:与get方向一样,但没有把target_buf内数据移除

2.1.3 方法

阻塞的是任务,非阻塞的是函数(函数有返回值)

2.2 代码示例

数据传输结构图:


代码:

数据类型、组件一:

传输的数据类型一定写的明明白白


组件1在run_phase是怎么做的呢

线程1:

  • 通过bp_port.put发送出去
    • port没有实现任何方法,这个方法不是port提供的,而是连接到的imp的组件2提供的
  • 更好的隔离性:完全看不到组件2,组件2的句柄,组件2的方法,完全不知道trans要发送到哪里;只知道要调用我的put方法

线程2:

  • try_get进行forever轮询


组件2:

为什么要多传递所例化在的类型?

  • 这样才能在port找到imp端口后,再通过imp找到组件二的方法

省略号省略例化


组件二方法定义续,端口连接

  • 对c1,c2例化
  • 对c1,c2连接
    • 从initor_port连接到target_imp


示例代码总结

2.3 调用端口方法之前的几个步骤是必不可少的

  • 定义端口
  • 实现对应方法啊
  • 在上层将端口进行连接

3 双向通信

3.1 概述

3.1.1 双向通信(bidirectional communication)

应用场景较少,目前大多数是单向通信

与单向通信相同的是,双向通信(bidirectional communication)的两端也分为initiator与target

但数据流向在端对端之间是双向的,两端同时扮演者着producer与consumer的角色,而initiator作为request发起方在发起request之后还会等待response返回

UVM双向端口分为以下类型:

1
2
3
4
5
6
7
8
9
10
11
12
//transport
uvm_blockong_transport_PORT
uvm_nonblockong_transport_PORT
uvm_transport_PORT
//master
uvm_blockong_master_PORT
uvm_nonblockong_master_PORT
uvm_master_PORT
//slave
uvm_blockong_slave_PORT
uvm_nonblockong_slave_PORT
uvm_slave_PORT

3.1.2 类型与表

  • transport双向通信
    • 调用task transport/nb_transport 使得在一次传输中同时完成req与rsp的返回
  • master/slave成对双向通信
    • 至少要调用两次对应的方法

3.2 代码示例

数据传输结构图:

transport双向通信方式,因为它明显区分于之前的单向通信方式


代码:

组件一:

组件二:

  • 下二语句内类型严格一致:

    • uvm_blocking_transport_imp #(类型)

    • task transport(类型)

env例化:

输出结果:

4 多向通信

4.1 概述

4.1.1 多向通信(multi-directional communication)

仍然是两个组件的通信,但是两个组件的通信由多个端口完成

数据传输结构图:

具体内容:

多向通信解决的问题:

  • comp2中需要实现两个put造成的命名冲突

  • 同时降低了耦合性

4.1.2 解决方案与命名方式

UVM通过端口宏声明端口来解决

它解决问题的核心在于让不同端口对应不同名的任务

  • 也就是让方法名不一样

UVM 为解决多向通信问题的宏按照端口名的命名方式分为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//put
`uvm_blockong_put_imp_decl(SFX)
`uvm_nonblockong_put_imp_decl(SFX)
`uvm_put_imp_decl(SFX)
//get
`uvm_blockong_get_imp_decl(SFX)
`uvm_nonblockong_get_imp_decl(SFX)
`uvm_get_imp_decl(SFX)
//peek
`uvm_blockong_peek_imp_decl(SFX)
`uvm_nonblockong_peek_imp_decl(SFX)
`uvm_peek_imp_decl(SFX)
//get_peek
`uvm_blockong_get_peek_imp_decl(SFX)
`uvm_nonblockong_get_peek_imp_decl(SFX)
`uvm_get_peek_imp_decl(SFX)
//transport
`uvm_blockong_transport_imp_decl(SFX)
`uvm_nonblockong_transport_imp_decl(SFX)
`uvm_transport_imp_decl(SFX)
//master
`uvm_blockong_master_imp_decl(SFX)
`uvm_nonblockong_master_imp_decl(SFX)
`uvm_master_imp_decl(SFX)
//slave
`uvm_blockong_slave_imp_decl(SFX)
`uvm_nonblockong_slave_imp_decl(SFX)
`uvm_slave_PORT

上述宏名称解释:

  • decl:表明要声明一个新的端口
  • SFX:独一无二的名称

4.2 代码示例

数据传输结构图:


代码:

宏声明与组件一

组件二

  • 方法实现
  • push到buf里
  • 旗语锁定buf

env(顶层)做连接

4.3 总结


与这种端口区别开,这种直接单向通信就可以

5 通信管道

5.1 问题及相关TLM组件和端口

TLM通信的实现方式都是端到端的,即target实现传输方法

  • 如何可以自己不是现这些传输方法同时使用到TLM呢

  • 对于monitor、coverage collector等组件存在一端到多端的传输如何解决

几个TLM组件和端口可以解决:

  • TLM FIFO
  • analysis port
  • analysis TLM FIFO
  • request & response 通信管道

5.2 TLM_FIFO组件

5.2.1 概述:TLM_FIFO是一个组件

put方法一般就是往FIFO里面送数据,我们使用UVM组件进行简单化就不用我们自己实现了


1
TLM FIFO uvm_tlm_fifo

TLM_FIFO是一个组件,继承于uvm_component,预先内置多个端口以及实现了多个对应方法供用户使用

  • 为什么是组件?

    • 因为只有组件才可以例化端口
  • 因此transaction不能例化该端口(组件),因为obj不是继承于comp

uvm_tlm_fifo功能类似于mailbox,只不过提供了各种端口使用

我们推荐在initiator例化put_port,或者get_peek_port来匹配uvm_tlm_fifo的端口类型。当然如果用户例化了其它类型的端口,uvm_tlm_fifo还提供了put、get、peek对应端口



uvm_tlm_fifo结构图:

5.2.2 TLM_FIFO 具有的端口

uvm_tlm_fifo功能类似于mailbox,只不过提供了各种端口使用

我们推荐在initiator例化put_port,或者get_peek_port来匹配uvm_tlm_fifo的端口类型。当然如果用户例化了其它类型的端口,uvm_tlm_fifo还提供了put、get、peek对应端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//put
uvm_put_imp #(T, this_type) blockong_put_export
uvm_put_imp #(T, this_type) nonblockong_put_export
//get
uvm_get_peek_imp #(T, this_type) blockong_get_export
uvm_get_peek_imp #(T, this_type) nonblockong_get_export
uvm_get_peek_imp #(T, this_type) get_export
//peek
uvm_get_peek_imp #(T, this_type) blockong_peek_export
uvm_get_peek_imp #(T, this_type) nonblockong_peek_export
uvm_get_peek_imp #(T, this_type) peek_export
//get_peek
uvm_get_peek_imp #(T, this_type) blockong_get_peek_export
uvm_get_peek_imp #(T, this_type) nonblockong_get_peek_export
uvm_get_peek_imp #(T, this_type) get_peek_export
  • _export:实际上都是实现的方法

5.3 Analysis Port

本节内容唯一一个一端到多端的组件,利用到观察者模式/广播模式实现


observer patten的核心:

  • 从一个initiator到多个target端
  • analysis port采取push模式,即从initiator端调用多个target端的write函数实现传输

Analysis Port连接结构图:

  • 我只管把消息发送出来

5.3.2 连接思路

1
2
3
initiator.ap.connect(target1.amp);
initiator.ap.connect(target2.amp);
initiator.ap.connect(target3.amp);

5.3 Analysis TLM FIFO

5.3.1 uvm_tlm_analysis_fifo

Analysis TLM FIFO就是Analysis Port中间加一个fifo的一种结构封装为的类

  • 第一部分是端到端
  • 第二部分是单向传递的port

uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo类,再此基础上添加了uvm_analysis_imp端口

1
uvm_analysis_imp #(T,uvm_tlm_analysis_fifo #(T)) analysis_export
  • 端口名称就叫analysis_export
  • FIFO参数类为T

Analysis TLM FIFO连接结构图:

5.3.2 连接思路

两部分连接:


  • ap
  • fifo1~3
  • target1~3
1
2
3
4
5
6
7
8
initiator.ap.connect(tlm_analysis_fifo1.analysis_export);
target1.get_port.connect(tlm_analysis_fifo1.get_export);

initiator.ap.connect(tlm_analysis_fifo2.analysis_export);
target2.get_port.connect(tlm_analysis_fifo2.get_export);

initiator.ap.connect(tlm_analysis_fifo3.analysis_export);
target3.get_port.connect(tlm_analysis_fifo3.get_export);

5.4 Request&Response管道

5.4.1 概述

  • 单端(单向通信):
    • TLM FIFO、analysis port、analysis TLM FIFO
  • 双向通信,匹配双向通信的FIFO:
    • request & response 通信管道

双向通信端口名:transport

需要在target实现transport()方法完成一次传输既发送req又接受rsp

UVM提供两种简便的通信管道作为数据缓冲区,既有TLM端口从外侧接收req和rsp,同时也有TLM端口供外侧获取req和rsp,这两种TLM通信管道分别是:

1
2
uvm_tlm_req_rsp_channel
uvm_tlm_transport_channel

5.4.2 端口

对uvm_tlm_req_rsp_channel而言,提供端口是单一方向的,为简洁只列出该类例化的端口:

1
2
3
4
5
6
7
8
9
10
11
uvm_put_export #(REQ) put_request_export;
uvm_put_export #(RSP) put_response_export;

uvm_get_peek_export #(REQ) get_peek_response_export;
uvm_put_peek_export #(RSP) get_peek_request_export;

uvm_analysis_port #(REQ) request_ap;
uvm_analysis_port #(RSP) response_ap;

uvm_master_imp #(REQ, RSP, this_type, uvm_tlm_fifo #(REQ), uvm_tlm_fifo #(RSP)) master_export
uvm_slave_imp #(REQ, RSP, this_type, uvm_tlm_fifo #(REQ), uvm_tlm_fifo #(RSP)) slave_export

例化这么多端口,使用户可以在使用成对的端口进行数据的存储和访问

5.4.3 示例结构一


uvm_tlm_req_rsp_channel内部例化了两个mailbox分别用来存储req和rsp

1
2
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端口


target不需要再实现对应的put,get,peek,对应的端口连接代码:

5.4.4 示例结构二:利用一个端口实现双向通信(master&slave)


•也可以利用另外一种连接方式:

1
2
initiator.master_port.connect(req_rsp_channel,master_export);
target.slave_port.connect(req_rsp_channel.slave_export);

通过所述的这些方式,我们可以实现initiator与target之间自由的request和 response传输,而这两种连接方式仍然需要分别调用两次方法才可以完成 request 和 response 的传输。


过程:

端口虽然减少了,调用的方法并没有减少

  • put(rsp)->get(rsq)

  • get(rsp)<-put(rsp)//target组件put时rr_channel才有数据


5.4.5 uvm_tlm_transport_channel

在uvm_t[m_req_rsp_channel的基础上,UVM又添加了具备transport端口的管道组件uvm_tlm_transport_channel类

它继承于 uvm_tlm_req_rsp_channel,并且新例化了transport端口:

1
uvm transport imp #(REQ, RSP, this type) transport_export

新添加的这个TLM FIFO组件类型是针对于一些无法流水化处理的 request和response传输,例如initiator一端要求每次发送完 request,必须等到response接收到以后才可以发送下一个request, 这时transport。方法就可以满足这一需求
如果将上面的传输方式进行修改,需要变化的是initiator端到 req_rsp_channel的连接,应该修改为:

1
initiator.transport_port.connect(transport_channel.transport_export)

•至于transport_channel和target之间的连接,则可以仍然保留之 前的单向传输连接方式

过程:

6 TLM2.0通信(了解)

6.1 概述

6.1.1 由TLM1.0到TLM2.0

  • TLM1.0用UVM各个组件之间连接
  • TLM2.0用于与SystemC进行连接

目前TLM开源包也是基于TLM2.0,且TLM2.0早于UVM成立的标准

6.1.2 对比、主要内容

什么时候用到TLM2.0,uvm与systemc做继承,把systemc作为reference model的时候

  • 因为systemc是纯设计模型,没有时间概念

纯验证环境中不需要TLM2.0

6.2 接口实现


6.2.1 传输方法

两种方法:

  • _fw:forward拿过来一个req
  • _bw:backward拿过来一个rsp

6.2.2 传输端口:端口类socket

socket由port,export和imp组合而成

6.2.3 端口类socket继承于uvm_port_base

6.3 传输数据

6.3.1 传输数据类型uvm_tlm_generic_payload

严格使用uvm_tlm_generic_payload类进行数据传输

这样才能与systemc无缝连接

6.3.2 uvm_tlm_generic_payload内部变量

6.3.3 其他数据类型:两种解决方法

6.4 时间标记

6.4.1 uvm_tlm_tim

原则上systemc可以自己创建一些时钟,当我们很少这样做,因为会大幅度降低仿真效率

6.5 通信代码示例

  • 数据类型固定所以不需要再指定数据传输类型
  • 自始至终rsq与rsp都是一个对象

顶层代码:

6.6 通信代码示例(有标注版)