TLM是啥,为什么要用TLM?
TLM 就是 UVM 各个组件之间,传递 transaction(数据包)的 "专用通道"。
Driver、Monitor、Sequencer、Scoreboard、RM、Coverage 这些组件互相之间不能直接调用函数、不能直接访问变量,必须通过 TLM 端口 发数据、收数据。
为什么不用赋值?
- 耦合太强
一个组件改了,另一个直接报错- 线程不安全
多线程同时读写会乱掉- 无法阻塞、同步
数据来了你不知道,只能轮询- 无法层次化、复用
换个环境就废了- 无法一对多广播
一个 monitor 数据要同时给 sb + coverage,直接赋值做不到
基于以上问题,UVM 强制:组件之间必须用 TLM 通信,禁止直接访问。
简单说,TLM 让 UVM 各个组件变成模块化、可插拔、可复用。
一. TLM使用规则
- TLM 是事务级建模,只传 transaction,不传信号
- 端口永远是主动的(port)
- 实现永远是被动的(imp / export)
- 必须成对使用:发送 ↔ 接收
- 阻塞(blocking):会等待,要等对方接受完才返回;
- 非阻塞(nonblocking):不等待,发完立刻返回
二.TLM接口类型
- Put 类:主动发送数据,单向收发,分阻塞/非阻塞
- 发送端调用:put(tr)
- 接口列表
uvm_blocking_put_port #(T)
uvm_nonblocking_put_port #(T)
uvm_put_port #(T)(阻塞 + 非阻塞) - 必须成对连接
port → export / imp - 接收端实现
task put(T tr)(阻塞)
function bit try_put(T tr)(非阻塞)
- Get / Peek 类:主动获取数据,单向收发,分阻塞/非阻塞
- get(tr):取出并删除
- peek(tr):只看不取
- 接口列表
uvm_blocking_get_port
uvm_blocking_peek_port
uvm_blocking_get_peek_port(最常用)
uvm_get_port、uvm_peek_port、uvm_get_peek_port - 必须成对连接
port → export / imp - 接收端实现
task get(output T tr)
task peek(output T tr)
- Analysis 类:广播发送,一对多广播,非阻塞,环境数据分发
- 只能调用 write(tr)
- 接口列表
uvm_analysis_port #(T)
uvm_analysis_export #(T)
uvm_analysis_imp #(T, IMP) - 接收端实现
function void write(T tr)
三.TLM FIFO
- uvm_tlm_fifo #(T)
- 支持:put_port/get_port
- 不支持 analysis_port
- uvm_tlm_analysis_fifo #(T) (常用,analysis_fifo = 万能 FIFO)
支持:analysis_port/put_port/get_peek_port
四.TLM 成对使用
发送端:主动发起请求,数据产生者;比如主动get数据,uvm_put_port;
接收端:被动响应,数据消费者;比如uvm_put_imp;
Export:中间传递端口,用于层次化连接;
- Put 成对
| 发送端 port | 连接 | 接收端(imp/export) | 调用方法 |
|---|---|---|---|
| uvm_blocking_put_port | → | uvm_blocking_put_imp / tlm_fifo.put_export | put() |
| uvm_nonblocking_put_port | → | uvm_nonblocking_put_imp | try_put() |
| put_port | → | put_imp | put() / try_put() |
连接规则:initiator.put_port.connect(target.put_imp);
比如drv发给rm
- Get / Peek 成对
| 获取端 port | 连接 | 接收端(imp/export) | 调用方法 |
|---|---|---|---|
| uvm_blocking_get_port | → | uvm_blocking_get_imp / fifo.get_export | get() |
| uvm_blocking_peek_port | → | uvm_blocking_peek_imp | peek() |
| uvm_blocking_get_peek_port | → | uvm_blocking_get_peek_imp / analysis_fifo | get() / peek() |
比如scoreboard从rm区数据做比对
- Analysis 成对
| 发送端 | 连接 | 接收端 | 调用 |
|---|---|---|---|
| analysis_port | → | analysis_imp / analysis_fifo | write() |
monitor发数据给rm和scoreboard
五.端口连接规则
port.connect(export);
export.connetc(imp);
- 必须在connect_phase连接;
- analysis port 不能直接连 get/peek port,必须加 uvm_tlm_analysis_fifo 中转。
- 所有端口泛型参数必须一致(都用 my_transaction)。
- put_port和put_imp:发送推数据
- get_port和get_imp:接收拉数据
- analysis_port和analysis_imp+write:广播专用,比如monitor
六.TLM优缺点
优点:
- 高抽象层级,脱离底层信号;不用时序,不用操作wire信号,直接传送事物,代码简洁,开发速度快,验证效率高;
- 组件解耦,标准化接口;比如drv,mon,rm等只靠TLM端口通信,互相不依赖内部实现,更换、复用、移植组件非常方便,符合面向对象+接口隔离思想;
- 天然支持层次化连接;port->export->imp层级转发,顶层env的connect_phase只做连接即可,内部组件不用改动;
- 阻塞/非阻塞通信灵活;阻塞适合同步交互、控制流水、天然同步时序;非阻塞适合异步收发、高吞吐;
- 便于后续收集覆盖率和调试打印;事物可以自带打印函数、时间戳、自动化比对等;
- 跨平台、可复用、易于升级;TLM是UVM标准,不同项目、不同IP之间通信协议统一,组件复用极强;
缺点:
- 抽象层级高,和真实硬件有差距;不是真实信号时序,无法反映精细时序、毛刺、跨时钟域和信号竞争等硬件细节;
- 仿真精度不足,低层缺陷难发现;只做事物级交互,时序违规、握手错误、协议细微违规容易遗漏,必须搭配monitor抓信号;
- 占用仿真内存高;比如频繁创建、拷贝、传送事物,大批量传输数据内存开销信号通信大;
- 初学者上手门槛高;端口种类多;连接关系和实现规则容易混淆,调试链路不通比较费时间;
- 阻塞TLM容易造成死锁:比如组件A等待B,B等待A,任务挂起死锁,需要仔细设计同步逻辑;
- analysis_port是广播型,可控性弱;比如一对多广播,无法单独控制某一个接口,流量大时容易冗余、影响性能仿真;
- 不适合物理层等底层验证;引脚级、时序、电平、串并转换等场景,TLM力不从心,还需要依赖信号级驱动和采样;
七.使用场景
组件内部环境之间用TLM,可高效传输数据,解耦复用;
验证环境与DUT交互用信号级,可保证硬件时序精度;
- monitor发包给scoreboard和rm,进行数据比对;
mon.put_port.connect(sb.put_imp);
使用uvm_tlm_fifo
// Monitor put_port → FIFO put_export
mon.put_port.connect(tlm_fifo.put_export);
// FIFO get_export → Scoreboard get_port
sb.get_port.connect(tlm_fifo.get_export);
或者uvm_tlm_analysis_fifo
// ========================
// blocking_put_port → ana_fifo.put_export
// ========================
mon.put_port.connect(ana_fifo.put_export);
// ========================
// get_peek_port ← ana_fifo.blocking_get_peek_imp
// ========================
sb.bgp_port.connect(ana_fifo.blocking_get_peek_imp);
- A环境drv和B环境的rm通信;
// RM 的 analysis port → FIFO 的 analysis_export
eA.rm.ap.connect(ana_fifo.analysis_export);
// Driver 的 get_peek_port → FIFO 的 blocking_get_peek_imp
eB.drv.bgp_port.connect(ana_fifo.blocking_get_peek_imp);