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中。

UVM相位执行顺序与objection控制点 结构相位(自顶向下,各执行一次) build_phase 创建组件/配置 connect_phase TLM连接 end_of_elaboration 拓扑检查 start_of_simulation 打印配置 运行相位(自底向上启动,run_phase与动态相位并行) run_phase(贯穿整个仿真,与下方动态相位并行) pre_reset 等待复位 reset 释放复位 post_reset 复位后稳定 configure 寄存器配置 main 主激励运行 shutdown 收尾/检查 objection 控制:raise_objection 阻止相位结束,drop_objection 允许相位推进 main_phase 中:raise → 启动激励 → 等待完成 → drop → 相位结束 → 仿真可退出 超时保护:set_timeout / phase_timeout,防止 objection 未 drop 导致仿真挂死 t=0 复位释放 配置完成 激励结束 仿真退出

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。

常见陷阱:在run_phase和main_phase中同时raise objection,会导致相位结束条件混乱。建议统一在动态相位中控制,run_phase只做后台监控或空实现。

可复用的相位控制检查清单

检查项正确做法错误后果
配置设置位置父组件build_phase中set子组件get到默认值
TLM连接位置connect_phase中connect连接为空,数据不通
复位释放reset动态相位中完成激励在复位期间丢失
寄存器配置configure相位中完成DUT工作模式错误
objection raise/drop成对出现,drop前确认激励完成仿真提前结束或挂死
超时保护set_timeout或fork超时分支仿真无限等待

掌握相位机制的关键,是理解“谁在什么时候做什么”以及“仿真何时可以结束”。build_connect决定平台结构,run_phase与动态相位决定运行节奏,objection决定结束条件。三者配合得当,才能让SoC验证平台在复位、配置、激励和收尾各阶段稳定可控。