phase.raise.objection (phase); seq=new(); seq.start(e.sgr);
phase.drop_objection(phase) ;
其中raise和drop是什么作用?是否重要?
在 UVM 的 run_phase(或其它消耗时间的 task phase)中,raise_objection 和 drop_objection 是极其重要、不可或缺的机制。它们直接决定了仿真会不会"提前结束",以及各个组件之间的运行时间能否对齐。
一、它们的作用是什么?
UVM 的 phase 机制(尤其是 run_phase 及其子 phase)靠 objection(反对/异议)机制 来判断"这个 phase 该不该结束"。
raise_objection(提起异议):告诉 UVM 调度器:"我现在正在干活(比如启动 sequence、驱动激励),这个 phase 还不能结束,请等我。"drop_objection(撤销异议):告诉 UVM 调度器:"我干完活了,这个 phase 可以结束了(如果别人也没有异议的话)。"
UVM 只有在所有组件都 drop 了 objection(即没有任何 objection 被 raise)之后,才会结束当前 phase,进入下一个 phase。
二、结合你的代码逐行解析
systemverilog
phase.raise_objection(phase);
seq = new();
seq.start(e.sgr);
phase.drop_objection(phase);
1. phase.raise_objection(phase);
phase是当前运行的 phase 对象(比如run_phase)。raise_objection是uvm_phase类的方法。- 传入的参数
phase(注意:标准写法通常是this,即当前组件)表示是谁提出了这个 objection 。- 标准写法是
phase.raise_objection(this);。 - 这里传了
phase本身,语法上不报错,但追踪 objection 来源时会有些奇怪。建议改成this。
- 标准写法是
- 执行这行后,UVM 的 objection 计数器加 1,当前 phase 不会结束。
2. seq = new();
- 创建一个 sequence 对象。
- 注意:这里用了
new()而不是type_id::create(),意味着这个 sequence 不走 factory,不会被 override。如果想让 sequence 支持工厂覆盖,应该用seq = my_seq::type_id::create("seq");。 - 这一步本身不消耗仿真时间,只是分配内存。
3. seq.start(e.sgr);
start是uvm_sequence的方法。e.sgr是 sequencer 的句柄(假设e是某个环境组件,sgr是它里面的 sequencer)。seq.start(e.sgr)会把 sequence 挂到 sequencer 上,然后开始执行 sequence 的body()任务。- 这是一个消耗仿真时间的操作 :sequence 会通过
start_item/finish_item把 item 发给 driver,driver 驱动 DUT,可能需要等几十、几百甚至几千个时钟周期。 - 所以必须在它前面
raise_objection,否则仿真会在时间 0 就结束,sequence 根本跑不完。
4. phase.drop_objection(phase);
- sequence 跑完后,撤销之前的 objection。
- 此时 objection 计数器减 1。
- 如果这是最后一个 objection,UVM 就会结束当前 phase,进入下一个 phase。
- 同样,标准写法是
phase.drop_objection(this);。
三、为什么说它们"极其重要"?
1. 没有 raise/drop,仿真会瞬间结束
UVM 的 run_phase 默认行为是:
如果没有任何 objection 被 raise,
run_phase会在时间 0 立即结束。
也就是说,如果你写:
systemverilog
task run_phase(uvm_phase phase);
seq = new();
seq.start(e.sgr);
endtask
仿真会在时间 0 直接跳过 run_phase,你的 sequence 根本来不及跑,DUT 也收不到任何激励。你会看到仿真"什么都没干就结束了"。
2. 它们控制 phase 的结束时机
UVM 的 phase 执行流程:
进入 run_phase
↓
所有组件并行执行 run_phase 任务
↓
等待所有 objection 被 drop
↓
run_phase 结束,进入下一个 phase
raise_objection相当于"拉住刹车";drop_objection相当于"松开刹车";- 只有所有"刹车"都松开,phase 才会结束。
3. 多组件并行时的同步作用
一个测试平台里可能有多个组件同时 raise objection:
test里启动 sequence;scoreboard里等待比对完成;monitor里等待采集完最后一笔数据。
只要任何一个组件还持有 objection,run_phase 就不会结束。这保证了所有组件都能把自己该干的事干完。
四、标准写法示例
systemverilog
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_sequence seq;
my_env e;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
e = my_env::type_id::create("e", this);
endfunction
task run_phase(uvm_phase phase);
// 1. 提起 objection,阻止 phase 结束
phase.raise_objection(this);
// 2. 创建并启动 sequence
seq = my_sequence::type_id::create("seq");
seq.start(e.sgr);
// 3. 撤销 objection,允许 phase 结束
phase.drop_objection(this);
endtask
endclass
注意顺序:
raise_objection必须在消耗时间的操作(如seq.start)之前;drop_objection必须在消耗时间的操作之后;- 如果中间有
fork...join或异常,要确保drop_objection一定被执行(可以用try...finally或仔细控制流程)。
五、常见误区与注意事项
| 误区 | 说明 |
|---|---|
在 build_phase 里用 objection |
build_phase 是 function phase,不消耗时间,objection 无效,也不该用 |
raise 了忘记 drop |
仿真会一直卡住,直到超时(phase_done 超时) |
drop 了没 raise |
UVM 会报 warning/error,objection 计数器不能为负 |
用 new() 创建 sequence |
不推荐,应该用 type_id::create(),否则无法被 factory override |
传参传 phase 而不是 this |
语法允许,但标准写法是传 this,便于调试和追踪 objection 来源 |
六、一句话总结
raise_objection是"等等我还没干完",drop_objection是"我干完了,可以走了"。它们决定了
run_phase什么时候结束,是 UVM 仿真时间推进的核心控制机制。没有它们,run_phase会在时间 0 直接结束,sequence 根本跑不起来。所以它们非常重要,而且是每个 UVM 验证工程师必须掌握的基础。