在 UVM (Universal Verification Methodology) 中,Objection 机制 是控制仿真生命周期的核心手段。简单来说,它决定了仿真什么时候开始停止。
在传统的 Verilog/SystemVerilog 仿真中,我们可能依赖 # 延时来结束,但在 UVM 这种组件化、动态化的环境中,需要一种更优雅的方式来确保所有的测试激励(Sequences)都执行完毕后再关闭仿真。
1. 核心原理:计数器机制
Objection 机制本质上是一个分布式计数器。
-
Raise Objection (提起): 计数器 +1。表示"我还在忙,现在不能停机"。
-
Drop Objection (落下): 计数器 -1。表示"我的任务做完了,可以考虑停机"。
当某个 Phase(通常是 run_phase)的 Objection 计数器变为 0 时,UVM 认为该阶段的任务已全部完成,随即关闭当前 Phase 并进入下一个 Phase。
2. 为什么叫objection?
在 UVM 中,之所以使用 "Objection"(反对/异议)这个词,其实蕴含了一个非常生动的逻辑比喻。可以把它想象成一场**"结项会议"**,仿真器(UVM Phase 控制器)是会议主持人。
1. 语义上的含义:一种"反对票"
在英文语境中,Raise an objection 的意思是"提出异议"或"反对某项提议"。
-
提议: UVM 仿真器默认会不断地问:"现在可以结束这个 Phase 吗?"
-
反对(Raise Objection): 如果一个组件(如 Test)还在忙,它就"举手反对"。含义是:"我反对现在结束 Phase,因为我还有任务没做完!"
-
撤回反对(Drop Objection): 当任务完成后,组件放下手说:"我不反对了,你可以随意结束。"
核心逻辑: 只要场上还有一个组件在"举手反对",会议(仿真阶段)就不能散场。
2. 形象的比喻:商场关门
想象一家商场准备晚上 10 点关门(Phase 尝试结束):
-
Raise Objection: 顾客 A 还在试衣服,他相当于向商场"提出异议",商场不能关门。
-
计数器增加: 此时又有顾客 B 进了洗手间(再次 raise)。目前有 2 个"反对关门"的信号。
-
Drop Objection: 顾客 A 试完衣服离开了(drop)。计数器变为 1。
-
Final Drop: 顾客 B 出来离开了(drop)。计数器变为 0。
-
Done: 商场保安(UVM Phase 控制器)看到没人反对了,锁门下班。
3. 术语总结
| 术语 | 动作含义 | 技术实质 |
|---|---|---|
| Raise Objection | 提起异议 | 内部计数器 +1,阻止当前 Phase 退出。 |
| Drop Objection | 落下(撤回)异议 | 内部计数器 -1,表示自己已准备好结束。 |
| All Dropped | 全体无异议 | 计数器归零,触发 Phase 切换逻辑。 |
3. 基本语法与用法
Objection 通常在 uvm_sequence 或 uvm_test 的 task body() 或 task run_phase() 中使用。
cpp
task run_phase(uvm_phase phase);
phase.raise_objection(this, "开始测试激励"); // 计数器 +1
// 执行具体的测试逻辑,如启动 sequence
seq.start(m_sequencer);
phase.drop_objection(this, "测试激励结束"); // 计数器 -1
endtask
4. Objection 的层级传播
Objection 具有向上传播的特性。
-
当一个 Component(如 Driver)提起 Objection 时,这个信号会沿着 UVM 树形结构一直传递到顶层的
uvm_top。 -
uvm_top维护着全局的计数器。 -
只要整个树中任何一个角落还有一个 Objection 没落下,仿真就不会停止。
5. Drain Time (排空时间)
有时候,当 Sequence 发送完最后一个数据包(Drop Objection)后,数据可能还在 DUT(待测设计)内部流动,尚未到达 Scoreboard。如果此时立即关闭仿真,会导致最后一项检查被跳过。
为了解决这个问题,UVM 提供了 Drain Time:
-
当计数器减为 0 后,UVM 不会立刻关闭 Phase,而是等待一段设定的仿真时间。
-
如果在等待期间没有新的 Objection 被提起,时间到后正式结束。
cpp
// 在 test 层设置 drain time
phase.get_objection().set_drain_time(this, 100ns);
6. 常见的使用误区与最佳实践
| 误区 | 解释与建议 |
|---|---|
| 在每一个 Component 里都用 | 不推荐。 过多的 Objection 会显著降低仿真速度(因为每次提起/落下都要遍历组件树)。 |
| 只在顶层控制 | 推荐。 通常只在最顶层的 uvm_test 或者主要的 uvm_sequence 中进行 Objection 控制。 |
| 忘记 Drop | 致命。 如果 raise 后由于代码 Bug 没执行到 drop,仿真会陷入死循环直到达到超时时间(Timeout)。 |
| 在 function phase 使用 | 错误。 Objection 主要用于耗时的 run_phase。对于 build_phase 等函数 phase,UVM 会自动管理。 |
7. 调试技巧
如果你的仿真停不下来,或者莫名其妙提前结束,可以使用以下命令行参数来追踪 Objection 的状态:
-
+UVM_OBJECTION_TRACE: 在终端打印出所有 Objection 的提起和落下记录。 -
这将显示是谁在什么时候
raise了 Objection,帮助你定位那个"不肯停下来"的组件。
8. 推荐使用objection的位置
| 组件 | 是否使用 Objection | 理由 |
|---|---|---|
| UVM Test | 是 (推荐) | 顶层控制,启动 Sequence 并确保其完成。 |
| Sequence | 是 (可选) | 适合自动化的 Sequence 行为(set_automatic_phase_objection)。 |
| Driver | 否 | 被动执行者,不应决定仿真的生死。 |
| Monitor/Scoreboard | 否 | 纯被动观察者,即使有数据在处理,通常也通过 Drain Time 等待。 |
raise_objection
drop_objection
get_objection
set_drain_time
问题:driver的main phase中需要使用objection机制吗
简单直接的回答是:通常不需要,也不建议在 Driver 的 main_phase(或 run_phase)中使用 Objection 机制。
虽然在技术上可行,但从 UVM 的设计哲学和实际工程效率来看,在 Driver 中控制 Objection 往往是"画蛇添足",甚至会带来隐患。
1. 为什么 Driver 不需要 Objection?
Driver 的角色是被动的消费者 。它的典型结构是一个 forever 循环,不断地从 Sequencer 获取事务并驱动。
-
生命周期不匹配 :Driver 的
run_phase或main_phase逻辑通常是死循环(直到仿真结束)。如果你在 Driver 的main_phase开头raise_objection而没有合适的时机drop,仿真将永远无法停止。 -
职责分工 :在 UVM 中,控制"什么时候结束测试"是 Test 层 或 Sequence 层的职责。Test 知道要发多少激励,而 Driver 只负责"怎么发"。
2. 为什么在 Driver 中使用 Objection 很危险?
如果在 Driver 的循环内部频繁 raise/drop objection,会产生以下问题:
-
性能开销 :Objection 的提起和落下涉及 UVM 树的层级遍历。Driver 每秒处理成千上万个事务,如果每个事务都 raise/drop,会显著拖慢仿真速度。
-
死锁风险 :如果在
get_next_item之前 raise,而在item_done后 drop,一旦 Sequencer 没数据了,Driver 会卡在get_next_item处,导致这最后一个 Objection 永远无法 drop,仿真因此挂起。