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);