3. TLM FIFO:uvm_tlm_fifo 的作用、深度配置、阻塞与非阻塞操作及组件间通信应用
在 UVM 验证环境中,组件之间的数据传输是验证平台的核心功能。TLM FIFO(First-In-First-Out)作为 UVM TLM 标准库中最基础、最常用的传输通道,承担着连接 producer 与 consumer、解耦组件执行时序、缓存数据的关键角色。本章将深入剖析 uvm_tlm_fifo 的作用机制、深度配置方法、阻塞与非阻塞操作的区别,以及它在实际组件间通信中的典型应用模式。
3.1 uvm_tlm_fifo 的作用与定位
uvm_tlm_fifo 是 UVM 提供的一个参数化、基于 TLM 接口的 FIFO 实现。它本质上是一个带有存储能力的 TLM 传输通道,既可以作为 put/get 操作的端点,也可以作为 export 端口对外提供服务。其核心作用包括:
- 数据缓存与缓冲:当 producer 产生数据的速度与 consumer 消费数据的速度不匹配时,FIFO 可以暂存数据,避免数据丢失或阻塞。
- 时序解耦:producer 和 consumer 可以在不同的仿真时间点执行 put 和 get 操作,FIFO 作为中间存储层,使双方无需同步等待。
- 接口适配:FIFO 可以连接不同类型的 TLM 端口(如 put_port 与 get_export),实现接口协议的转换与匹配。
- 事务级建模:在事务级模型中,FIFO 作为通信通道,支持事务的排队、优先级处理以及延迟模拟。
uvm_tlm_fifo 是一个参数化类,其声明为 class uvm_tlm_fifo #(type T=int) extends uvm_component。类型参数 T 决定了 FIFO 中存储的事务数据类型,默认值为 int。
3.2 FIFO 的深度配置
FIFO 的深度决定了其最多可以缓存多少个事务。深度配置直接影响验证平台的资源消耗、数据吞吐量以及死锁风险。UVM 提供了两种配置方式:
| 配置方式 | 语法/方法 | 说明 |
|---|---|---|
| 构造函数参数 | new(name, parent, depth) |
在创建 FIFO 实例时直接指定深度,最常用。 |
| 运行时配置 | set_depth(int depth) |
通过 FIFO 对象的方法动态修改深度,需谨慎使用。 |
| 默认深度 | 不指定时默认深度为 1 | 仅能缓存一个事务,适用于同步传输场景。 |
// 创建深度为 10 的 FIFO,用于传输 my_transaction 类型
uvm_tlm_fifo #(my_transaction) fifo_h;
fifo_h = new("fifo_h", this, 10);
// 运行时修改深度(不推荐在仿真过程中频繁使用)
fifo_h.set_depth(20);
uvm_tlm_fifo #(T)::new("name", this, 0),此时深度为 0 表示无限制。
3.3 阻塞与非阻塞操作
uvm_tlm_fifo 支持两种类型的 put/get 操作:阻塞操作和非阻塞操作。这两种操作模式决定了当 FIFO 满(put 时)或空(get 时)时,调用方的行为。
3.3.1 阻塞操作
阻塞操作会在条件不满足时挂起当前进程,直到操作可以成功执行。这是最常用的操作模式,适用于 producer 和 consumer 可以等待的场景。
| 方法 | 功能 | 阻塞条件 |
|---|---|---|
put(T t) |
将事务 t 放入 FIFO | FIFO 满时阻塞,直到有空位 |
get(output T t) |
从 FIFO 取出事务到 t | FIFO 空时阻塞,直到有数据 |
peek(output T t) |
查看 FIFO 头部事务但不移除 | FIFO 空时阻塞 |
// 阻塞 put 示例
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
// 产生事务
tr = my_transaction::type_id::create("tr");
// 阻塞放入 FIFO,若 FIFO 满则等待
fifo_h.put(tr);
end
endtask
// 阻塞 get 示例
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
// 阻塞从 FIFO 取出事务,若 FIFO 空则等待
fifo_h.get(tr);
// 处理事务
process_transaction(tr);
end
endtask
3.3.2 非阻塞操作
非阻塞操作会立即返回,无论操作是否成功。调用方需要检查返回值来判断操作是否完成。这种模式适用于需要轮询或超时控制的场景。
| 方法 | 功能 | 返回值 |
|---|---|---|
try_put(T t) |
尝试放入事务 t | 成功返回 1,失败返回 0 |
try_get(output T t) |
尝试取出事务到 t | 成功返回 1,失败返回 0 |
can_put() |
查询是否可以放入 | 有空位返回 1,否则 0 |
can_get() |
查询是否可以取出 | 有数据返回 1,否则 0 |
// 非阻塞 try_put 示例
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
tr = generate_transaction();
// 尝试非阻塞放入,若失败则丢弃或重试
if (fifo_h.try_put(tr)) begin
`uvm_info("PRODUCER", "Transaction put successfully", UVM_MEDIUM)
end else begin
`uvm_warning("PRODUCER", "FIFO full, transaction dropped")
end
end
endtask
// 非阻塞 try_get 示例
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
// 尝试非阻塞取出
if (fifo_h.try_get(tr)) begin
process_transaction(tr);
end else begin
// FIFO 空,执行其他任务或等待
#10;
end
end
endtask
- 阻塞操作:适用于 producer 和 consumer 速率匹配、可以相互等待的场景。代码简洁,逻辑清晰。
- 非阻塞操作:适用于需要超时控制、多路复用、或者 producer/consumer 不能长时间阻塞的场景。需要额外处理失败情况。
- 混合使用:可以在同一个 FIFO 上混合使用阻塞和非阻塞操作,但需注意避免死锁。
3.4 FIFO 在组件间通信中的应用
在 UVM 验证平台中,uvm_tlm_fifo 最常见的应用场景是连接 driver 与 monitor、sequencer 与 driver,以及 scoreboard 与 coverage collector 之间的数据传输。下面通过一个典型的验证平台架构图来展示 FIFO 的通信位置。
3.4.1 典型应用模式:Sequencer 与 Driver 之间的 FIFO
在 UVM 中,Sequencer 和 Driver 之间通过 seq_item_port 和 seq_item_export 连接,底层实际上使用了一个 uvm_tlm_fifo 作为传输通道。这个 FIFO 的深度通常为 1,但可以通过配置修改。
// 在 agent 中连接 sequencer 和 driver
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
function void build_phase(uvm_phase phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction
function void connect_phase(uvm_phase phase);
// 通过 TLM FIFO 连接 sequencer 和 driver
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
3.4.2 典型应用模式:Monitor 与 Scoreboard 之间的 FIFO
Monitor 通过 analysis port 将采集到的事务广播给多个 subscriber(如 Scoreboard、Coverage Collector)。每个 subscriber 内部都有一个 uvm_tlm_fifo 来缓存接收的事务。
// Monitor 端:通过 analysis port 发送事务
class my_monitor extends uvm_monitor;
uvm_analysis_port #(my_transaction) mon_ap;
function void build_phase(uvm_phase phase);
mon_ap = new("mon_ap", this);
endfunction
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
// 从 DUT 接口采样事务
tr = sample_transaction();
// 通过 analysis port 广播
mon_ap.write(tr);
end
endtask
endclass
// Scoreboard 端:通过 analysis export 接收事务
class my_scoreboard extends uvm_scoreboard;
uvm_tlm_fifo #(my_transaction) fifo_h;
function void build_phase(uvm_phase phase);
// 创建深度为 100 的 FIFO,用于缓存 monitor 发送的事务
fifo_h = new("fifo_h", this, 100);
endfunction
task run_phase(uvm_phase phase);
my_transaction tr;
forever begin
// 阻塞从 FIFO 取出事务进行处理
fifo_h.get(tr);
compare_transaction(tr);
end
endtask
endclass
3.5 FIFO 状态查询与调试
uvm_tlm_fifo 提供了一系列状态查询方法,方便在仿真过程中监控 FIFO 的使用情况,对于覆盖率分析和调试非常有用。
| 方法 | 返回值 | 说明 |
|---|---|---|
used() |
int | 当前 FIFO 中缓存的事务数量 |
is_empty() |
bit | FIFO 是否为空 |
is_full() |
bit | FIFO 是否已满 |
size() |
int | FIFO 的最大深度 |
flush() |
void | 清空 FIFO 中的所有事务 |
// 在覆盖率分析中使用 FIFO 状态
function void check_fifo_coverage();
if (fifo_h.is_full()) begin
`uvm_info("FIFO_COV", $sformatf("FIFO full at time %0t, used=%0d", $time, fifo_h.used()), UVM_LOW)
// 记录 FIFO 满的覆盖率事件
cov_group.sample();
end
if (fifo_h.used() > fifo_h.size() * 0.8) begin
`uvm_warning("FIFO_COV", $sformatf("FIFO usage exceeds 80%%: %0d/%0d", fifo_h.used(), fifo_h.size()))
end
endfunction
3.6 本章小结
uvm_tlm_fifo 是 UVM 验证平台中组件间通信的核心通道。通过合理配置 FIFO 深度、选择合适的阻塞/非阻塞操作模式,可以构建高效、稳定的数据传输链路。在实际应用中,FIFO 不仅承担数据缓存的功能,更是实现组件解耦、时序隔离和覆盖率分析的关键基础设施。掌握 FIFO 的配置与使用,是构建高质量 UVM 验证平台的基础。