2. TLM 端口与接口:put/get/transport 端口、master/slave 接口、UVM 中 TLM 端口基类
在 UVM 验证环境中,TLM(Transaction Level Modeling)端口是组件之间进行事务级通信的核心通道。本章深入剖析 TLM 端口的分类、接口协议以及 UVM 中对应的基类实现,为后续构建适配器与覆盖率收集模型奠定基础。
2.1 TLM 端口分类与通信模式
TLM 端口按照数据传输的方向和发起方式,主要分为三类基本端口:put、get 和 transport。每种端口对应一种特定的通信语义。
| 端口类型 | 通信方向 | 操作语义 | 典型应用场景 |
|---|---|---|---|
| put | 发起方 → 接收方 | 生产者推送事务给消费者 | 激励发生器向驱动器发送事务 |
| get | 发起方 ← 接收方 | 消费者从生产者拉取事务 | 监视器从分析端口获取数据 |
| transport | 双向 | 请求-响应同步交互 | CPU 模型发起读/写操作并等待返回 |
关键区别: put 和 get 是单向非阻塞或阻塞操作,而 transport 是双向阻塞操作,调用方在事务完成前会一直等待。
2.2 Master/Slave 接口角色
在 TLM 通信中,Master 和 Slave 定义了通信双方的角色与责任。Master 主动发起事务,Slave 被动响应事务。这种角色划分与端口类型结合,形成了四种标准接口组合。
记忆要点: Master 端总是发起操作(put/get/transport 的发起方),Slave 端总是实现操作(put/get/transport 的实现方)。
| 接口名称 | Master 端口 | Slave 端口 | 通信方向 |
|---|---|---|---|
| put 接口 | put_port | put_export / put_imp | Master → Slave |
| get 接口 | get_port | get_export / get_imp | Master ← Slave |
| transport 接口 | transport_port | transport_export / transport_imp | Master ↔ Slave |
| analysis 接口 | analysis_port | analysis_export / analysis_imp | 广播(一对多) |
2.3 UVM TLM 端口基类体系
UVM 提供了一套完整的 TLM 端口基类,位于 uvm_tlm_if 包中。这些基类采用参数化设计,支持任意事务类型。下图展示了核心端口基类的继承与组合关系。
2.4 端口连接规则与代码示例
UVM 中 TLM 端口的连接遵循严格的层级规则:port → export → imp。port 只能连接 export,export 只能连接 imp,imp 是最终实现事务处理方法的组件。
常见错误: 直接将 port 连接到 imp 会导致编译错误。必须通过 export 进行中间连接,除非使用
uvm_*_imp 直接作为实现端口。
以下代码展示了一个典型的 put 端口连接示例:
// 生产者组件:拥有 put_port
class producer extends uvm_component;
`uvm_component_utils(producer)
uvm_put_port #(my_transaction) put_port;
function new(string name, uvm_component parent);
super.new(name, parent);
put_port = new("put_port", this);
endfunction
virtual task run_phase(uvm_phase phase);
my_transaction tr;
// 创建并发送事务
tr = my_transaction::type_id::create("tr");
put_port.put(tr);
endtask
endclass
// 消费者组件:实现 put_imp
class consumer extends uvm_component;
`uvm_component_utils(consumer)
uvm_put_imp #(my_transaction, consumer) put_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
put_imp = new("put_imp", this);
endfunction
// 实现 put 方法
virtual function void put(my_transaction t);
`uvm_info("CONSUMER", $sformatf("Received transaction: %s", t.sprint()), UVM_LOW)
endfunction
endclass
// 在更高层组件中连接
class env extends uvm_component;
producer p;
consumer c;
function void connect_phase(uvm_phase phase);
p.put_port.connect(c.put_imp); // 直接连接 imp(允许)
endfunction
endclass
2.5 端口类型选择决策流程
在实际验证环境中,选择合适的 TLM 端口类型取决于通信需求。下图给出了端口选型的决策流程。
2.6 端口与适配器的关系
在后续章节中,TLM 适配器将利用这些端口实现事务格式的转换。适配器通常包含一个 put_port 或 get_port 用于接收原始事务,经过转换后通过另一个端口发送给目标组件。理解端口基类的参数化机制和连接规则,是正确实现适配器覆盖率收集模型的前提。
适配器端口设计要点:
- 输入端口类型应与上游组件输出端口匹配(通常为 put_port 或 analysis_port)
- 输出端口类型应与下游组件输入端口匹配
- transport 端口适用于需要同步响应的总线协议转换
- analysis 端口适用于广播式覆盖率采样点分发