如何保证UVM每时间步checker在所有monitor执行完成后运行
UVM验证环境checker同周期调度顺序问题解决方案
问题背景
现有UVM验证环境包含多个带monitor和analysis port的agent,所有agent的analysis port均连接至checker组件,用于适配设计自带的旁路逻辑(输入端口检测到事务后同一周期直接输出),处理同周期同时出现input_txn与output_txn的场景。
两类常见checker架构均存在调度风险:
Design 1:单进程顺序检查
class my_checker extends uvm_component; //boiler plate uvm... task run_phase(); forever begin check_inputs(); check_outputs(); @(posedge vinft.clk); end endtask function check_inputs(); input_txn_c txn; if (input_analysis_fifo.try_get(txn)) begin // non-blocking try_get() //do check pending_txn_cnt++; end endfunction function check_outputs(); output_txn_c txn; if (output_analysis_fifo.try_get(txn)) begin //non-blocking try_get() assert(pending_txn_cnt > 0); pending_txn_cnt--; end endfunction endclass
该架构固定先检查输入再检查输出,同周期事务处理逻辑符合预期,但存在执行顺序风险:仿真器可能在output_agent的run_phase执行完成后、input_agent的run_phase执行前就调度checker的run_phase,导致断言误触发。
Design 2:并行进程检查
class my_checker extends uvm_component; //boiler plate uvm... task run_phase(); fork check_inputs(); check_outputs(); join_none endtask task check_inputs(); input_txn_c txn; forever begin input_analysis_fifo.get(txn); //blocking get() //do check pending_txn_cnt++; end endtask task check_outputs(); output_txn_c txn; forever begin output_analysis_fifo.get(txn); //blocking get assert(pending_txn_cnt > 0); pending_txn_cnt--; end endtask endclass
该架构通过fork-join_none并行启动输入、输出检查进程,无法保证input_txn被优先处理:若output_txn先被处理,断言会因未识别到同周期存在input_txn而误触发。
解决方案
UVM没有内置自动实现「当前时间步所有monitor执行完成后再启动checker」的专属机制,但可以通过UVM原生同步原语零侵入实现每周期专属检查屏障效果,完全规避进程调度顺序不确定导致的断言误触发问题,核心是用uvm_barrier做显式的每周期同步屏障,从根源上消除同时间槽内的进程调度不确定性,不需要修改现有checker的检查逻辑。
具体实现步骤
- 在验证环境顶层(env层)例化一个
uvm_barrier实例:- 将屏障的等待阈值设置为需要同步的monitor总数量(比如输入agent monitor+输出agent monitor共2个,阈值就设为2)
- 开启屏障的自动重置属性,保证每次放行后计数自动清零,适配逐周期同步的需求
- 修改所有参与同步的monitor采样逻辑:
每个monitor的run_phase中,在时钟沿完成信号采样、事务组包、调用analysis_port.write(txn)将事务发送到checker侧的analysis fifo之后,立刻调用barrier.wait_for(),进入等待状态,等其他所有monitor完成当前周期的事务发送操作。 - 修改checker的run_phase触发逻辑:
移除原有直接等待posedge clk就执行检查的逻辑,改为先等待barrier.wait_for(),等屏障放行(即所有monitor都已将当前周期的事务写入fifo)之后,再按固定顺序调用check_inputs()、check_outputs()完成当前周期检查,之后等待下一个时钟沿进入下一轮循环即可。
方案优势
- 彻底规避Design 2架构下并行进程调度顺序不确定、输出事务被优先处理的问题,检查顺序完全可控
- 彻底规避Design 1架构下checker被优先调度、monitor还未写入当前周期事务就启动检查的问题,所有fifo读操作都在所有monitor事务发送完成后执行
- 不依赖仿真器的特定调度规则,在所有主流UVM仿真器下行为一致
- 不需要修改现有的事务检查逻辑,仅调整检查启动的同步时机,完全适配旁路逻辑同周期输入输出检查需求
不建议依赖
#0延迟、修改phase调度优先级这类隐式方法实现顺序控制,这类方法在不同仿真器、不同编译选项下可能出现行为差异,稳定性远低于显式屏障同步。
内容的提问来源于stack exchange,提问作者Melandru's Square
相关产品推荐
相关产品推荐

