路科验证的个人空间 https://blog.eetop.cn/1561828 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

UVM通信篇之一:TLM通信概论

热度 1已有 5501 次阅读| 2018-1-28 13:22 |个人分类:验证系统思想|系统分类:芯片设计

在目前SoC设计的几大挑战当中,最令人关注的莫过于:

  • 爆炸性增长的复杂度(然而被互联网大佬们无视还号称要硬件免费的表示这真得受到了一万点伤害)

  • 快速面向市场的压力(路桑在通信芯片这个ICer最容易“绝顶”的细分领域)

  • 天价的流片费用和项目资金压力(一次流片失败恐怕创业小公司就要关门了)


所以,硬件公司真得是被逼得没有退路只能为了生计发自肺腑地喊出要最快最准地流片,不允许流片失败。也正因为这么大的压力,在SoC开发中没有迭代一说,也没有流行的软件敏捷开发一说,更没有版本发布一说,因为芯片项目的流程环节跟软件开发的小步走快速试错快速迭代是冲突的。每一代芯片都需要比上一代芯片在性能指标和功耗方面有明显提升,这样终端商、整机商才会为此买单。一个调动上百人的芯片硬件开发项目在实际执行过程中是极其复杂,而且充满了非常多的不确定性。对于项目经理,他需要时刻照顾的就是人、时间和钱。而这里面,时间往往是首当其冲,关系到整个芯片能否如期上市。

在目前的SoC开发流程中,有两个地方对于项目的助推起到了关键作用:

  • 系统原型

  • 芯片验证


系统原型一般是通过硬件功能描述文档,来描述硬件的行为,而该行为的要求不同于RTL模型的特点。系统原型是可以提供一个准确到硬件比特级别、按照地址段访问、不依赖于时钟周期的模型。该模型通常基于SystemC语言,而系统原型中的各个模块通过TLM实现宽松时间范围内的数据包传输。


芯片验证则是在RTL模型初步建立之后,通过验证语言和方法学例如SV/UVM来构建验证平台。该平台的特点是验证环境整体基于面向对象的开发方式,而且组件之间的通信也基于TLM通信方式,而在driver与硬件的接口之间,又需要将TLM抽象事务降解到基于时钟的信号驱动级别。同时,在验证过程中,系统原型也可以作为参考模型置入到UVM环境当中,减轻验证人员重复构建参考模型的压力,提供整体开发的复用性。关于SystemC原型在UVM环境中嵌入的应用,我们将在后面的《TLM2通信即SystemC接口》一节中详细介绍。


可以从芯片开发流程中看到,在系统原型阶段和芯片验证阶段均使用了TLM通信方式。前者是为了更快地实现硬件原型之间的数据通信,后者是为了更快地实现验证组件之间的数据通信。仿真速度是TLM对项目进度的最大贡献,同时TLM传输中的事务又可以保证足够大的信息量和准确性。


所以,读者看到这里首先需要明确的是,TLM并不是某一种语言的产物,而是作为一种提高数据传输抽象级的标准存在的。高抽象级的数据通信,可以用来表示相当宽松时间跨度内的硬件通信数据,而通过将低颗粒硬件传送周期内的数据打包为一个大数据,非常有利于整体环境的仿真速度。实际上,TLM的运用越来越广泛起来,包括emulator同硬件仿真器的协同仿真框架中,也建议这种方式来降低混合环境之间的通信频率,提供整体的运算效率。


TLM是一个基于事务(transaction)的通信方式,通常在高抽象级的语言中被引用作为模块之间的通讯方式,例如SystemC或者UVM。TLM成功地将模块内的计算和模块之间的通信从时间跨度方面剥离开了。在抽象语言建模的体系中,各个模块通过一系列并行的进程实现,并且通过通信和计算来模拟出正确的行为。而如果要提高系统模型整体的仿真性能,需要考虑两个方面。一个是建模自身的运算优化,另外一个是模型之间的通信优化。前者需要依靠开发者的经验还有开发工具的性能分析工具,来逐步优化模型自身;后者则可以将通信的频率降低,内容体积增大的方式,来减少不同进程之间的同步带来的资源损耗。而TLM正是从通信优化的角度提出的一种抽象通信方式。


在实现的过程中,TLM通信需要两个通信的对象,这两个对象分别称之为initiator object和target object。区分它们的方法在于,谁首先发起通信的要求,谁就属于initiator,而谁作为发起通信的响应方,谁就属于target。在初学过程中,读者们还应该注意,通信发起方并不代表了transaction的流向起点,即不一定数据是从initiator流向target,也可能是从target流向了initiator。因此,按照transaction的流向,我们又可以将两个对象分为producer和consumer。区分它们的方法是,数据从哪里产生,它就属于producer,而数据流向了哪里,它就属于consumer。


从下面的这张图可以看出,initiator与target的关系同producer与consumer的关系,不是固定的。而有了两个参与通信的对象之后,用户需要将TLM的通信方法在target一端中实现,以便于initiator将来作为发起方可以调用target内的通信方法,实现数据传输。在target实现了必要的通信方法之后,最后一步需要将两个对象进行连接。这需要首先在两个对象内创建端口,继而在更高的层次中将这两个对象进行连接。


所以,抽象来看TLM通信的步骤可以分解为:

  1. 分辨出initiator和target,producer和consumer。

  2. 在target中实现TLM通信方法。

  3. 在两个对象中创建TLM端口。

  4. 在更高的层次中将两个对象的端口进行连接。


上面列出的这些步骤,读者可以通过下面这个例子进一步消化,并且加深对于上述的几个关键名词的认识:

  • initiator

  • target

  • producer

  • consumer

  • transaction direction


在深入这个例子之前,对于TLM端口间的通信方向和类型名称,读者们需要先认识一下。从数据流向的方向来看,传输的方向可以分为单向(unidirection)和双向(bidirection)。

  • 单向传输:由initiator发起request transaction。

  • 双向传输:由initiator发起request transaction,传送至target;而target在消化了request transaction后,也会发起response transaction,继而返回给initiator。


端口的按照类型可以划分为三种:

  • port:经常作为initiator的发起端,也凭借port,initiator才可以访问target中实现的TLM通信方法。

  • export:作为initiator和target中间层次的端口。

  • imp:只能是作为target接收request的末端,它无法作为中间层次的端口,所以imp的连接无法再次延伸。


如果将传输方向和端口类型加以组合,那么读者变可以理解之前在《类库地图》中介绍的,关于TLM端口的继承树:


综合下来,一共可以分为六类端口类型:

  • uvm_UNDIR_port #(trans_t)

  • uvm_UNDIR_export #(trans_t)

  • uvm_UNDIR_imp #(trans_t, imp_parent_t)

  • uvm_BIDIR_port #(req_trans_t, rsp_trans_t)

  • uvm_BIDIR_export #(req_trans_t, rsp_trans_t)

  • uvm_BIDIR_imp #(req_trans_t, rsp_trans_t, imp_parent_t)


从单向端口而言,port和export对象作为request发起方,需要同时传递transaction类型作为数据参数,而imp对象则需要作为request接收方,不但需要transaction类型,也需要传递例化它的component的类型。从双向端口而言,则整体上参数表中考虑到双向传输的因素,将传输类型transaction进一步拆分为了request transaction类型和response transaction类型。


首先来看看下面的例子中,关于TLM端口的类型、层次和对应的连接。可以从对应的连接关系中初步得出TLM端口连接的一般做法:

  • 在initiator端中例化port,在中间层次例化export,而在target端例化imp。

  • 多个port可以连接到同一个export或者imp;但是单个port或者export无法连接多个imp。这可以抽象为多个initiator可以对同一个target发起request,但是同一个initiator无法连接多个target。

  • port应为request起点,imp应为request终点,而中间可以穿越多个层次。基于单元组件的自闭性考虑,建议在这些穿越的中间层次中声明export,继而通过一级一级的连接实现数据最终的通路。

  • port可以连接port、export或者imp;export可以连接export或者imp;imp只能作为数据传送的重点,无法扩展连接。


下面我们就上图的TLM端口连接关系,给出实例代码供用户参考。



从这段例码中可以得出关于准备TLM通信的常规步骤:

  1. 定义TLM传输中的数据类型,上面分别定义了request类和response类。

  2. 分别在各个层次中的component中声明和创建TLM端口对象。上面的端口类型只选择了一小部分,而关于完整的端口列表将在下一节《单向、双向及多向通信》中为读者们做详细阐述。

  3. 在各个层次中通过connect()函数完成当前层次与下一级层次之间的端口连接。

  4. 在各个imp端口创建的类中要实现各自需要提供给initiator的可调用方法。例如,在comp2中由于有一个uvm_nonblocking_put_imp #(request, comp2) nbp_imp,因此需要实现两个方法try_put()和can_put();而comp4中有一个 uvm_blocking_get_imp #(request, comp4) bg_imp,则需要实现对应的方法get()。读者要注意的是,这些方法是必须实现的,否则端口即使连接也无法实现数据的传输。对于更多的imp类型与其对应的方法定义,读者们可以在下节中有更详细的参考。


谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。


1

点赞

刚表态过的朋友 (1 人)

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 253

    粉丝
  • 25

    好友
  • 33

    获赞
  • 45

    评论
  • 访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-5-6 23:41 , Processed in 0.017417 second(s), 12 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部