UVM重点归纳(二)之TLM通信

1 概念

initiator:发起通信请求的对象;

target:接收通信请求的对象;

producer:数据开始产生的对象;

consumer:数据最终流向的对象;

initiator和target的关系同producer和consumer的关系不是固定的,initiator作为数据的发起端,如果其使用的方法是put/try_put/can_put,则initiator也将作为producer;另外,如果其使用的方法是get/try_get/can_get/peek/try_peek/can_peek,则initiator也将作为consumer。

TLM通信步骤分为:

  • 定义TLM传输中的数据类型,定义request类和response 类。
  • 分别在各个层次的component中声明和创建TLM端口对象。
  • 在imp端口类中要实现需要提供给initiator的可调用方法。
  • 通过connect()函数完成端口之间的连接。

需要注意的是,必须在imp端口类中实现对应方法,否则端口即使连接也无法实现数据传输。

端口类型分为:

  • port: 经常作为initiator的发起端,initiator凭借port才可以访问target中的TLM方法;
  • export:作为initiator和target的中间层次;
  • imp:只能作为target接收request的末端。

端口的可通信方向: 

  • port 可以连接 port 、 export 或 imp: export 可以连接 export 或 imp; imp 只能作为数据传送的终点, 无法扩展连接。
  • 多个 port 可以连接到同一个 export或imp; 但单个 port或export 无法连接多个imp 。这可以理解为多个initiator 可以对同一个 target 发起 request, 但是同一个initiator 无法连接多个target。

关于TLM概念的更多详细介绍可点击以下链接:

TLM通信icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124348590

2 单向通信

 单向通信 (unidirectional communication) 指的是从 initiator 到 target 的数据流向是单一方向的, 或者 initiator 和 target 只能扮演 producer 和 consumer 中的一个角色。

实例: 

1.首先comp1例化了两个port端口:
•     uvm_blocking_put_port #(itrans) bp_port; 
•     uvm_nonblocking_get_port #(otrans) nbg_port; 

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

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

4.env1环境将comp1与comp2连接之前, 需要在comp1中调用对应的方法:

•     this.bp_port.put(itr) 
•     this.nbg_port.try_get (otr) 

5.接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。

•     c1.bp_port.connect(c2.bp_imp); 
•     c1.nbg_port.connect(c2.nbg_imp);  

更多端口介绍点击以下链接:

单向通信icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124353341

3 双向通信 

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

双向端口按照通信握手方式可以分为: 

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

实例: 

 transport双向通信方式

1.首先comp1例化了port端口:
•     uvm_blocking_transport_port #(itrans,otrans) bt_port; 

2.comp2作为target则相应例化了两个对应的imp端口:
•     uvm_blocking_transport_imp #(itrans,otrans,comp2) bt_imp; 

3.env1环境将comp1与comp2连接之前, 需要在comp2中实现两个端口对应的方法:
•     task transport(itrans req, output otrans rsp); 

4.env1环境将comp1与comp2连接之前, 需要在comp1中调用对应的方法:

•     this.bt_port.transport(itr,otr) ;

5.接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。

•     c1.bt_port.connect(c2.bt_imp); 
master双向通信方式 

1.首先comp1例化了port端口:
•     uvm_blocking_master_port #(itrans,otrans) bm_port; 

2.comp2作为target则相应例化了两个对应的imp端口:
•     uvm_blocking_master_imp #(itrans,otrans,comp2) bm_imp; 

3.env1环境将comp1与comp2连接之前, 需要在comp2中实现两个端口对应的方法:
•     task put(itrans req); 

•     task get(output otrans rsp);

4.env1环境将comp1与comp2连接之前, 需要在comp1中调用对应的方法:

•     this.bm_port.put(itr) 
•     this.bm_port.get (otr) 

5.接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。

•     c1.bm_port.connect(c2.bm_imp); 

slave双向通信方式 

1.首先comp1例化了port端口:
•     uvm_blocking_slave_port #(itrans,otrans) bs_port; 

2.comp2作为target则相应例化了两个对应的imp端口:
•     uvm_blocking_slave_imp #(itrans,otrans,comp2) bs_imp; 

3.env1环境将comp1与comp2连接之前, 需要在comp2中实现两个端口对应的方法:
•     task put(otrans rsp); 

•     task get(output itrans req);

4.env1环境将comp1与comp2连接之前, 需要在comp1中调用对应的方法:

•     this.bs_port.put(otr) 
•     this.bs_port.get (itr) 

5.接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。

•     c1.bs_port.connect(c2.bs_imp); 

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

transport:

•     task transport(itrans req, output otrans rsp); 

master:

•     task put(itrans req); 

•     task get(output otrans rsp);

slave:

•     task put(otrans rsp); 

•     task get(output itrans req);

 更多端口介绍点击以下链接:

双向通信icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124353341

3 多向通信 

多向通信指的是, 如果initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。

范式:

`uvm_blocking_put_imp_decl(SFX)

`uvm_nonblocking_put_imp_decl(SFX)

注意:该通信方法主要针对imp端口提出的,因为当有两个uvm_blocking_put_imp端口, 我们对于端口例化可以给不同名字, 连接也可以通过不同名字来索引, 但问题在于comp2中需要实现两个task put (itrans t), 又因为不同端口之间要求在imp端口侧实现专属方法, 这就造成了方法命名冲突, 即无法在comp2中定义两个同名的put任务。UVM通过端口宏声明方式来解决这一问题, 它解决问题的核心在于让不同端口对应不同名的任务, 这样便不会造成方法名的冲突。

实例: 

1.首先为避免target端口声明冲突,定义了两个宏;

•    `uvm_blocking_put_imp_decl(_pl);

•    `uvm_blocking_put_imp_decl(_p2);

2.在comp1例化了两个port端口:
•     uvm_blocking_put_port #(itrans) bp_port1; 
•     uvm_blocking_put_port #(itrans) bp_port2; 

3.comp2作为target则相应例化了两个对应的imp端口:
•     uvm_blocking_put_imp_p1 #(itrans, comp2) bp_imp_p1; 
•     uvm_blocking_put_imp_p2 #(itrans, comp2) bp_imp_p2; 

4.env1环境将comp1与comp2连接之前, 需要在comp2中实现两个端口对应的方法:
•     task put_p1(itrans t) ;
•     task put_p2(itrans t);

5.env1环境将comp1与comp2连接之前, 需要在comp1中调用对应的方法:

•     this.bp_port1.put(itr) 
•     this.bp_port2.put (itr) 

6.接下来env1对两个组件的端口进行了连接, 这使得comp1在run phase可以通过自身端口间接调用comp2中定义的端口方法。

•     c1.bp_port1.connect(c2.bp_imp_p1); 
•     c1.bg_port2.connect(c2.bg_imp_p2);  

注意:对于 comp1调用 put()方法, 它只需要选择 bp_port1 或 bp_port2, 而不需要更替 put()方法名, 即仍然按照 put()来调用而不是 put_pl()或 put_p2()。

总结:用户只需要在例化多个imp端口的组件中实现不同名称的方法,使其与对应imp类型名保持一致。而对于port端口一侧的组件,则不需关心调用的方法名称,因为该方法名并不会发生改变。所以通过这种方式可以防止通信方法名的冲突,从而解决多向通信的问题。

 更多端口介绍点击以下链接:

多向通信icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124353341

 4 通信管道

利用通信管道可以解决以下问题:

1.对于仅仅存放数据的方法,不需要再去手动定义,通信管道已将这些方法固定在内部;

2.通信管道实现了一端连接多端

 4.1 TLM_FIFO

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

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

 

4.2 Analysis port 

Analysis port 的核心在于: 

第一, 这是从一个 initiator 端到多个 target 端的方式;

第二, analysis port 采取的是 "push" 模式, 即从 initiator 端调用多个 target 端的 write()函数实现数据传输。

在 initiator 端调用wirte()函数时, 实际上它是通过循环的方式将所有连接的 target 端内置的 write()函数进行了调用。由于函数立即返回的特点, 无论连接多少个 target 端, initiator 端调用 write()函数总是可以立即返回的。这里稍微不同于之前单一端口函数调用的是,即使没有target 与之相连,调用 write() 函数时也不会发生错误。

4.3 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()函数.

4.4 Request&Response 通信管道 

关于通信管道相关内容请点击以下链接:

通信管道icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124353341 

5 TLM2.0 

更多关于TLM2.0的介绍请点击以下链接:

TLM2.0icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_45680021/article/details/124368966

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创芯人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值