4. UVM相位机制与仿真控制
UVM的相位(phase)机制是验证平台从“类库”走向“可运行系统”的骨架。它把仿真过程切分为若干有序阶段,让组件在正确的时间做正确的事:先建结构、再连接口、再配置、再运行、最后收尾。对SoC验证而言,相位机制不只是语法约定,它直接决定了复位释放时机、寄存器配置顺序、激励启动条件以及仿真何时可以安全结束。
相位分层:结构相位与运行相位
UVM相位分为两大类。第一类是结构相位,包括build、connect、end_of_elaboration、start_of_simulation等,它们自顶向下执行,每个组件只执行一次,适合创建子组件、建立TLM连接、打印拓扑。第二类是运行相位,包括run_phase以及与之并行的12个动态相位(pre_reset、reset、post_reset、pre_configure、configure、post_configure、pre_main、main、post_main、pre_shutdown、shutdown、post_shutdown),它们自底向上启动,适合驱动激励、监控响应、控制复位与配置流程。
关键点在于:run_phase与动态相位是并行关系,不是先后关系。如果同时在两处驱动同一接口,会产生竞争。工程上通常二选一:简单平台只用run_phase;SoC平台涉及复位、配置、主运行、关断等阶段时,优先使用动态相位,把复位释放、寄存器配置、主激励、收尾分别放到reset、configure、main、shutdown中。
build_phase与connect_phase的工程要点
build_phase是唯一自顶向下执行的相位,父组件先于子组件创建。因此配置对象(config object)和配置数据库(config_db)的设置必须在父组件的build_phase中完成,子组件才能在自身build_phase中通过uvm_config_db#(T)::get()取到值。常见错误是在子组件build中set、父组件get,方向反了,导致取到默认值。
connect_phase自底向上执行,适合建立monitor到scoreboard、driver到sequencer等TLM连接。注意:connect_phase中不要做耗时操作,也不要启动激励。连接关系一旦建立,后续相位不应再修改。
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
my_scoreboard sb;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 父层设置配置,子层才能get到
uvm_config_db#(int)::set(this, "agent.driver", "pre_num", 8);
agent = my_agent::type_id::create("agent", this);
sb = my_scoreboard::type_id::create("sb", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent.mon.ap.connect(sb.imp);
endfunction
endclass
objection机制:仿真结束的控制权
UVM不会自动知道激励何时结束。它依靠objection机制判断:只要还有组件raise了objection且未drop,run_phase(或对应动态相位)就不会结束;当所有objection都被drop后,相位进入结束流程,最终调用$finish()退出仿真。
典型用法是在sequence的body()中raise/drop,或在test的main_phase中控制。推荐用phase.raise_objection(this)和phase.drop_objection(this)成对出现,且drop前必须确保激励已发完、响应已收齐。
class my_test extends uvm_test;
`uvm_component_utils(my_test)
task main_phase(uvm_phase phase);
my_seq seq;
phase.raise_objection(this, "start main seq");
seq = my_seq::type_id::create("seq");
seq.start(env.agent.sqr);
// 等待DUT响应全部返回
#200ns;
phase.drop_objection(this, "main seq done");
endtask
endclass
复位、配置、超时与仿真结束的典型问题
SoC验证中,复位释放和寄存器配置往往有严格时序。若在reset阶段就raise objection并启动主激励,DUT可能尚未退出复位,导致激励丢失。正确做法是:在reset动态相位中只做复位释放和等待,在configure相位完成寄存器配置,在main相位才raise objection启动主激励。
超时是另一个高频问题。如果某个组件raise后忘记drop,仿真会一直挂住。UVM提供phase.phase_done.set_drain_time()和全局超时设置。工程上常在base_test中设置uvm_top.set_timeout(10ms, 0),或在main_phase中配合fork...join_any加超时分支,超时后打印objection状态并强制drop。
可复用的相位控制检查清单
| 检查项 | 正确做法 | 错误后果 |
|---|---|---|
| 配置设置位置 | 父组件build_phase中set | 子组件get到默认值 |
| TLM连接位置 | connect_phase中connect | 连接为空,数据不通 |
| 复位释放 | reset动态相位中完成 | 激励在复位期间丢失 |
| 寄存器配置 | configure相位中完成 | DUT工作模式错误 |
| objection raise/drop | 成对出现,drop前确认激励完成 | 仿真提前结束或挂死 |
| 超时保护 | set_timeout或fork超时分支 | 仿真无限等待 |
掌握相位机制的关键,是理解“谁在什么时候做什么”以及“仿真何时可以结束”。build_connect决定平台结构,run_phase与动态相位决定运行节奏,objection决定结束条件。三者配合得当,才能让SoC验证平台在复位、配置、激励和收尾各阶段稳定可控。