2. TLM接口基础:put/get/transport等核心TLM操作原语

TLM(Transaction-Level Modeling)接口是UVM验证平台中组件间通信的基石。它通过一组标准化的操作原语,将事务级数据的传输、同步与时间消耗抽象出来,使验证组件(如driver、monitor、scoreboard)之间能够以统一的方式进行数据交换,而无需关心底层信号级细节。

2.1 核心操作原语概览

TLM 1.0 定义了三种最基本的单向通信原语:put、get 和 transport。它们分别对应数据的生产、消费和组合操作。下表列出了这些原语的语义、方向及典型使用场景:

原语 方向 语义描述 典型使用场景
put 发起方 → 接收方 发起方将事务推送给接收方,接收方阻塞直到完成处理。 driver从sequencer获取sequence item后,将其发送给DUT接口。
get 发起方 ← 接收方 发起方向接收方请求一个事务,接收方阻塞直到有数据可用。 monitor从接口收集事务后,将其发送给scoreboard进行分析。
transport 发起方 ↔ 接收方 组合操作:先发起一个请求事务,再等待一个响应事务返回。 寄存器模型通过总线UVC对DUT进行读写操作(request + response)。
关键理解: put/get 是单向原语,数据流只有一个方向;transport 是双向原语,内部封装了 request 和 response 两个阶段。在适配器封装中,正确区分单向与双向原语是避免接口不匹配的首要条件。

2.2 put 与 get 原语详解

put 和 get 是 TLM 中最基础的两个操作。它们都支持 阻塞(blocking)和 非阻塞(non-blocking)两种调用方式,分别对应不同的时序要求。

2.2.1 阻塞 put (blocking_put)

调用方调用 put(T t) 后,会一直阻塞,直到接收方完成对该事务的处理并返回。接收方通过实现 put() 方法来定义处理逻辑。

// 发起方(Producer)调用示例
class producer extends uvm_component;
  uvm_blocking_put_port #(my_transaction) put_port;

  task run_phase(uvm_phase phase);
    my_transaction tr;
    tr = my_transaction::type_id::create("tr");
    // ... 填充事务数据 ...
    put_port.put(tr);  // 阻塞直到接收方处理完成
  endtask
endclass

// 接收方(Consumer)实现示例
class consumer extends uvm_component;
  uvm_blocking_put_imp #(my_transaction, consumer) put_export;

  function void put(my_transaction t);
    // 处理事务 t 的逻辑
    $display("Received transaction: %s", t.sprint());
  endfunction
endclass

2.2.2 阻塞 get (blocking_get)

调用方调用 get(output T t) 后,会一直阻塞,直到接收方有事务可供取出。接收方通过实现 get() 方法来提供数据。

// 发起方(Consumer)调用示例
class consumer extends uvm_component;
  uvm_blocking_get_port #(my_transaction) get_port;

  task run_phase(uvm_phase phase);
    my_transaction tr;
    get_port.get(tr);  // 阻塞直到有事务可用
    // 分析事务 tr
  endtask
endclass

// 接收方(Producer)实现示例
class producer extends uvm_component;
  uvm_blocking_get_imp #(my_transaction, producer) get_export;
  my_transaction fifo[$];

  function void get(output my_transaction t);
    t = fifo.pop_front();  // 从内部FIFO取出事务
  endfunction
endclass

2.3 transport 原语详解

transport 原语将 put 和 get 组合成一个原子操作,用于实现 请求-响应 模式。它包含两个阶段:

  1. 请求阶段: 发起方将请求事务发送给接收方。
  2. 响应阶段: 接收方处理请求后,将响应事务返回给发起方。
// transport 原语接口定义
class uvm_transport #(type REQ = int, type RSP = int);
  // 阻塞 transport
  task transport(REQ request, output RSP response);
    // 实现:处理 request,生成 response
  endtask

  // 非阻塞 transport
  function bit transport(REQ request, output RSP response);
    // 返回 1 表示立即完成,0 表示无法完成
  endfunction
endclass

// 实际使用示例:寄存器读写
class reg_adapter extends uvm_reg_adapter;
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    bus_transaction tr = bus_transaction::type_id::create("tr");
    tr.addr = rw.addr;
    tr.data = rw.data;
    tr.kind = (rw.kind == UVM_READ) ? READ : WRITE;
    return tr;
  endfunction

  function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    bus_transaction tr;
    $cast(tr, bus_item);
    rw.data = tr.data;
    rw.status = tr.status;
  endfunction
endclass
适配器封装要点: 当使用 transport 原语封装寄存器访问时,必须确保适配器内部的 reg2bus 和 bus2reg 方法能够正确地将寄存器操作(读/写)映射为总线事务的 request 和 response。任何映射错误都会导致仿真数据不一致。

2.4 阻塞与非阻塞变体

TLM 为每个原语提供了阻塞和非阻塞两种变体,以适应不同的时序需求。下表总结了它们的差异:

变体 接口名称 返回值 阻塞行为 适用场景
阻塞 put blocking_put 无(task) 调用方阻塞直到完成 同步数据传输,如driver驱动DUT
非阻塞 put nonblocking_put bit(function) 立即返回,1=成功,0=失败 零延时试探,如monitor采样
阻塞 get blocking_get 无(task) 调用方阻塞直到有数据 scoreboard等待比对数据
非阻塞 get nonblocking_get bit(function) 立即返回,1=有数据,0=无数据 轮询检查FIFO状态
阻塞 transport blocking_transport 无(task) 调用方阻塞直到响应返回 寄存器同步读写
非阻塞 transport nonblocking_transport bit(function) 立即返回,1=完成,0=未完成 流水线式总线访问

2.5 TLM操作原语关系图

下图展示了 put、get、transport 三种原语在验证组件间的数据流关系,以及它们与阻塞/非阻塞变体的组合方式:

TLM核心操作原语关系图 发起方 (Producer/Consumer) 接收方 (Consumer/Producer) put (单向推送) get (单向拉取) transport (请求+响应) 阻塞与非阻塞变体 阻塞 (blocking_*) task,调用方阻塞等待 非阻塞 (nonblocking_*) function,立即返回状态 组合使用 适配器灵活选择

2.6 适配器封装中的常见陷阱

陷阱1:原语方向不匹配。 例如,在适配器中错误地将 driver 的 put 接口连接到了 scoreboard 的 get 接口,导致数据流方向相反,仿真时出现死锁或数据丢失。
陷阱2:阻塞与非阻塞混用。 在同一个连接中,一方使用 blocking_put,另一方使用 nonblocking_put_imp,会导致编译通过但运行时行为异常(如非阻塞端无法处理阻塞端的等待)。
陷阱3:transport 的 request/response 类型不匹配。 适配器在 reg2bus 中返回的事务类型与 bus2reg 中期望的类型不一致,导致 $cast 失败。

2.7 小结