【UVM入门笔记(三)】TLM通信

一、TLM通信概述
  1. 系统原型阶段和芯片验证阶段均使用了TLM通信方式。前者为了更快地实现硬件原型之间的数据通信,后者为了更快地实现验证组件之间的数据通信。
  2. TLM是一种提高数据传输抽象级的标准,可以用来表示宽松时间跨度内的硬件通信数据。通过将低颗粒硬件周期内的数据打包成一个大数据,可以有效地提升整体环境的仿真速度。
  3. 基本概念
    1)两个通信对象:initial(发起通信请求的一方)和target(发起通信的响应方)。
    2)按照transaction的流向区分两个对象:producer(数据产生一方)和consumer(数据流向方)。
    initialtarget的关系同producerconsumer的关系是不固定的。
  4. TLM通信步骤
    1)在两个对象中创建TLM端口
    2)在target中实现TLM通信方法
    3)在更高的层次中将两个对象的端口进行连接
  5. 分类
    ①按照端口类型:
    1)port:作为initiator的发起端
    2)export:作为initialtarget中间层次的端口
    3)imp:只能作为target接收request的末端,无法再次延伸。
    ②按照数据流向
    1)单向传输
    2)双向传输
    👉数据流向和端口类型加以组合,TLM端口共可以分为六类。
uvm_UNDIR_port #(trans_t)
uvm_UNDIR_export #(trans_t)
uvm_UNDIR_imp #(trans_t, imp_parent_t)  // 不仅需指定transaction类型,还需指定所在component类型。
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)
  1. 端口使用注意问题
    1)多个port可以连接到同一个export或者imp,但是单个port或者export无法连接多个imp。
    2)port可以连接port、export或者imp;export可以连接export或者imp;imp只能作为数据传送的终点,无法扩展连接。
二、单向通信

在这里插入图片描述

  • 阻塞传输方式(blocking前缀)对应方法类型为task;非阻塞传输方式(nonblocking前缀)对应方法类型为function。
  • peek同get的区别在于peek只是没把原数据移除。
  • try_xxx()方法和can_xxx()方法的区别在于can_xxx()函数先试探target是否可以接收数据,如果可以,再通过try_xxx()函数发送,提高数据发送的成功率。
//example
class comp1 extends uvm_component;
  uvm_blocking_put_port #(itrans) bp_port;
  uvm_nonblocking_get_port #(otrans) nbg_port;
  ...
  // inside run phase
  this.bp_port.put(xx);
  ...
  if(this.nbg_port.try_get(xx)==1) ...;
  ...
endclass
class comp2 extends uvm_component;
  uvm_blocking_put_imp #(itrans, comp2) bp_imp;
  uvm_nonblocking_get_imp #(otrans, comp2) nbg_imp;
  ...
  task put(itrans t);
  ...
  endtask
  function bit try_get(output otrans t);
  ...
  endfunction
endclass

class env1 extends uvm_env;
  comp1 c1;
  comp2 c2;
  ...
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    c1.bp_port.connect(c2.bp_imp);
    c1.nbg_port.connect(c2.nbg_imp);   //左侧initiator, 右侧target
  endfunction: connect_phase
endclass
三、双向通信
  • 双向通信中的两端同时扮演着producer和consumer的角色,而initiator作为request发起方,在发起request之后,还会等待response返回。
    在这里插入图片描述
四、多向通信
  • 多向通信仍然是两个组件之间的通信,是针对initiator与target之间相同TLM端口数目超过一个时的处理解决办法。
  • 通过端口宏声明方式解决。eg. `uvm_blocking_put_imp_decl(SFX)
`uvm_blocking_put_imp_decl(_p1)
`uvm_blocking_put_imp_dec1(_p2)
class comp1 extends uvm_component;  //组件1不受任何影响,目前并不知道最后连接到哪里
  uvm_blocking_put_port #(itrans) bp_port1;
  uvm_blocking_put_port #(itrans) bp_port2;
  ...
  // inside run_phase
  this.bp_port1.put(xx);
  ...
  this.bp_port2.put(xx);
endclass
class comp2 extends uvm_component;
  uvm_blocking_put_imp_p1 #(itrans, comp2) bt_imp_p1;
  uvm_blocking_put_imp_p1 #(itrans, comp2) bt_imp_p2;
  semaphore key;   //对于共享资源,做互斥访问的保护
  ...
  task put_p1(itrans t);
    key.get();
    ...
  endtask
  task put_p2(itrans t);
    key.get();
    ...
  endtask
endclass
class env1 extends uvm_env;
  comp1 c1;
  comp2 c2;
  ...
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    c1.bp_port1.connect(c2.bt_imp_p1);
    c1.bp_port2.connect(c2.bt_imp_p2);
  endfunction: connect_phase
endclass
五、管道通信

👉解决问题:1)不自己实现target一端需要实现的传输方法;2)解决一端到多端的传输

  1. TLM_FIFO
    多数情况下,需要实现的传输方法都是相似的,方法的主要内容即是为了实现一个数据缓存功能。TLM FIFO uvm_tlm_fifo类是一个新组件,继承于uvm_component类,而且已经预先内置了多个端口以及实现了多个对应方法供用户使用。
    在这里插入图片描述
  2. Analysis Port
    1)针对一个initiator端到多个target端的方式;需要调用的是write()函数来实现数据传输。
    2)类似于其它TLM端口,按照传输方法和端口方向组合可以将analysis port分为uvm_analysis_portuvm_analysis_export以及uvm_analysis_imp
    3)在initiator端调用write()函数时,实际上通过循环的方式将所有连接的target端内置的write()函数进行了调用。
  3. Analysis TLM FIFO
    uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo,这表明它本身具有面向单一TLM端口的数据缓存特性,而同时该类又有一个uvm_analysis_imp端口analysis_export并实现了write()函数。
    注意,虽然名为export但实际上是imp类型的端口,并且下图中的target端的端口类型变成了port类型,整体的数据流向与原来的是相同的。
    在这里插入图片描述
  4. 双向通信管道
    1)uvm_tlm_req_rsp_channel
    在这里插入图片描述2)uvm_tlm_transport_channel
    uvm_tlm_req_rsp_channel继承于uvm_tlm_req_rsp_channel
    在这里插入图片描述
六、TLM2通信
  • TLM2强大的传输特性包括: 1)双向的阻塞或者非阻塞接口;2)时间标记;3)统一的数据包。
  • 为了区别与TLM1对于端口类型的称谓,UVM将TLM2.0端口类型称为socket。
七、同步通信元件
  1. uvm_event
    1)不同组件可以共享同一个uvm_event,不需要通过跨层次传递uvm_event对象句柄来实现共享(这不符合组件环境封闭的原则),该共享方式是通过uvm_event_pool这一全局资源池来实现。
    2)如果要传递数据,用户可以定义扩展于uvm_object的数据子类,并通过uvm_event::trigger(T data=null)来传递数据对象。在等待uvm_event一侧的组件则需要通过uvm_event::wait_trigger_data(output T data)来获取该对象。
    3)用户可以扩展uvm_event_callback类,定义uvm_event被trigger前后的调用方法pre_trigger()post_trigger()pre_trigger()要有返回值,如果返回值为1,则不会继续向下执行。
    4)如果无法确定在等待事件之前,事件是否已经被trigger,那么还可通过wait_ptrigger()wait_ptrigger_data()来完成等待。
    5)组件之间的常规数据流向是通过TLM通信方法实现的。对于uvm_object和uvm_component之间或者uvm_object之间的同步可以借助uvm_event实现。

  2. uvm_barrier
    👉对多个组件进行同步协调,uvm_barrier可以设置一定的等待阈值xx.set_threshod(2),当有不少于该阈值的进程在等待该对象xx.wait_for()时才会触发该事件,同时激活所有正在等待的进程,使其可以继续进行。

  3. uvm_callback
    类的复用除了通过继承还可以通过回调函数实现。
    1)为了保证调用uvm_callback的组件类型T与uvm_callback类型CB保持匹配,可通过宏声明uvm_register_cb(T, CB)来实现。
    2)uvm_callback建立了回调函数执行的层次性,不再是在T中直接调用某个回调函数,而是通过宏uvm_do_callback(T, CB, METHOD)来声明。
    3)在执行回调方法时,依赖的是已经例化的uvm_callback对象。

class env1 extends uvm_env;
  comp1 c1;
  cb1 m_cb1;
  cb2 m_cb2;
  ...
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    c1 = comp1::type_id::create("c1", this);
    uvm_callback #(comp1)::add(c1, m_cb1);
    uvm_callback #(comp2)::add(c1, m_cb2);
  endfunction
endclass
<think>嗯,用户这次的问题很专业啊,是关于UVMTLM穿层通信的实现。用户应该是数字验证工程师,正在搭建复杂的验证平台。从ta直接抛出术语"cross-layer communication"来看,至少是有中级以上UVM经验了。 上次我给的快速排序示例可能让用户误解了引用格式,这次要特别注意:用户提供的引用[^1]其实是UVM宏的用法示例,和当前问题无关。真正的重点在于理解TLM端口穿透层次结构的机制。 用户的核心诉求其实是:如何让不同层级(比如component层和sequence层)的UVM组件直接通信。这确实是个高级话题,常规的TLM端口只能在同一层级传递。让我拆解下关键点: 首先想到的是TLM FIFO的桥接方案。这个最经典,通过中间FIFO连接两个不同层级的port和export。比如monitor在env层,sequence在seqr层,就在中间加个analysis_fifo。不过用户要的是"穿层",可能更关注直接连接方式。 啊,config_db方案差点忘了!这个其实更常用,把高层组件的接口指针set到低层,实现自顶向下的穿透。但要注意线程安全问题,特别是从sequence线程访问component对象时。 说到sequence层访问,sequence_item自带层次信息吗?其实没有。所以sequence发item时,需要显式指定目标路径,或者通过p_sequencer获取父组件指针。这个细节必须强调,很多用户会在这里踩坑。 最后是analysis端口广播方案。这个适合监控类场景,但要注意subscriber可能收到过多无关数据,需要加过滤器。用户如果做性能验证可能会需要这个。 突然想到用户可能遇到的典型场景:在virtual sequence里想直接读取DUT寄存器的实时值。这时候最优雅的方案其实是层穿透:1)monitor捕获寄存器值 2)通过config_db存到scoreboard 3)sequence通过p_sequencer->p_env->scoreboard访问。这个例子值得展开。 对了,线程安全必须单独强调。上次帮另一个用户debug时就发现,sequence里直接调用component的方法导致死锁。应该提醒用户用uvm_event或uvm_barrier做同步。 最后生成的问题要覆盖不同场景:第一个问题针对架构设计,第二个解决具体实施难点,第个则是debug技巧。用户现在可能正在搭建平台,后续肯定需要这些。</think>在UVM中实现TLM穿层通信(跨层次通信),核心是通过**端口连接、配置机制和层次路径引用**打破组件层级限制。以下是具体实现方法: --- ### 一、TLM端口穿透层级(基础方法) 通过`uvm_tlm_analysis_port`和`uvm_tlm_analysis_fifo`实现跨层次通信: ```systemverilog // 场景:Monitor(在Agent层)需直接传递数据给Scoreboard(在Env层) class my_monitor extends uvm_monitor; uvm_analysis_port #(my_transaction) ap; // 声明分析端口 virtual task run_phase(uvm_phase phase); my_transaction tr; ap.write(tr); // 发送数据到高层 endtask endclass class my_env extends uvm_env; my_agent agent; my_scoreboard scb; uvm_tlm_analysis_fifo #(my_transaction) fifo; // 跨层FIFO virtual function void connect_phase(uvm_phase phase); agent.monitor.ap.connect(fifo.analysis_export); // Monitor端口→FIFO fifo.get_port.connect(scb.analysis_imp); // FIFO→Scoreboard endfunction endclass ``` **原理**:通过中间FIFO桥接不同层级的端口,避免直接层级依赖[^1]。 --- ### 二、UVM ConfigDB全局配置(动态穿透) 使用`uvm_config_db`传递对象句柄或接口: ```systemverilog // Step1: 在高层组件(如Env)中存放句柄 class my_env extends uvm_env; virtual my_interface vif; function void build_phase(uvm_phase phase); uvm_config_db#(virtual my_interface)::set(this, "*", "vif", vif); endfunction endclass // Step2: 在底层组件(如Driver)获取句柄 class my_driver extends uvm_driver; virtual my_interface vif; function void build_phase(uvm_phase phase); if(!uvm_config_db#(virtual my_interface)::get(this, "", "vif", vif)) `uvm_error("NO_VIF", "Interface not set!") endfunction endclass ``` **优势**:任意层级组件均可通过`ConfigDB`访问共享资源[^1]。 --- ### 、层次化路径引用(直接访问) 通过`UVM组件路径`直接引用对象: ```systemverilog // 在Sequence中访问Env层的寄存器模型 class my_seq extends uvm_sequence; my_reg_model reg_model; task body(); uvm_component tmp; tmp = uvm_root::get().find("uvm_test_top.env.reg_model"); // 绝对路径查找 if (!$cast(reg_model, tmp)) `uvm_fatal("CAST_ERR", "Type cast failed") reg_model.reg_field.write(status, data); // 直接操作寄存器 endtask endclass ``` **风险**:路径依赖性强,需严格维护层次结构[^1]。 --- ### 四、Analysis端口广播(订阅模式) 利用`UVM订阅机制`实现一对多穿透: ```systemverilog // Monitor广播数据,多个组件订阅 class my_monitor extends uvm_monitor; uvm_analysis_port #(my_transaction) ap; // ... 发送数据 endclass class my_subscriber extends uvm_component; uvm_analysis_imp #(my_transaction, my_subscriber) imp; virtual function void write(my_transaction tr); // 处理跨层数据 endfunction endclass // Env层连接 agent.monitor.ap.connect(env.subscriber.imp); // 直接跨层连接 ``` --- ### 关键注意事项 1. **线程安全** 跨线程通信(如Sequence→Driver)需使用`uvm_event`或`uvm_barrier`同步。 2. **循环依赖** 避免组件间双向引用,推荐单向数据流(如Monitor→Scoreboard)。 3. **性能优化** 高频数据传输建议使用`FIFO深度优化`或`压缩协议`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值