You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UVM驱动中非阻塞赋值引发uvm_info打印竞态问题咨询

UVM驱动中uvm_info打印与非阻塞赋值的竞态问题解决

问题现象

我正在实现一个同步UVM驱动,已将驱动信号对齐时钟边沿,采用非阻塞赋值避免竞态条件。运行驱动后,打印输出为:

myCmd.start: 1
myCmd.start: 0

但预期输出应为:

myCmd.start: 0
myCmd.start: 1

波形查看器中信号工作正常,仅uvm_info()打印结果异常。若在打印前添加#1延迟:

#1; uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.dut_command), UVM_LOW)

即可得到正确打印值。请问仅用非阻塞赋值不足以处理UVM驱动中uvm_info()的竞态问题吗?该如何解决此问题?


附代码

驱动代码

class my_master_driver_c extends uvm_driver#(my_frame_c);

  virtual my_if my_master_vif;
   //...
  task run_phase(uvm_phase phase);

    while (1) begin
      begin

        forever begin
          @(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset))
          seq_item_port.get_next_item(req);
          drive_transfer(req);
          #10;
          seq_item_port.item_done(req);
        end
      end
    end
  endtask
  //...
  
  task drive_transfer (input my_frame_c trans);    
    
    @(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset));
    my_master_vif.dut_command <= 0;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.dut_command), UVM_LOW)
    
    @(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset));
    my_master_vif.dut_command <= 1;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.dut_command), UVM_LOW)
    //...

  endtask : drive_transfer

  //...

endclass                  

接口代码

interface my_if 
   (
    input clock_my, 
          reset,
    inout wire w_dut_command,
    output reg dut_clock_i,
    //...

    );

  timeunit 1ps;
  timeprecision 1ps;    

  assign dut_clock_i   = clock_my;

  logic dut_command = 'bz;
        assign w_dut_command  = dut_command;    

  clocking driver_cb @(posedge clock_my);
    default input #1 output #1;

    inout dut_command;

  endclocking    

endinterface : my_if

问题分析与解决

根源

非阻塞赋值(<=)的更新发生在NBA(Non-Blocking Assignment)调度区域,而uvm_info()属于立即执行区域。时钟边沿触发后,你先执行非阻塞赋值,紧接着调用uvm_info读取信号值——此时非阻塞赋值的更新还未生效,读取到的是信号的旧值,这就是打印和波形不一致的原因。

波形查看器显示的是NBA区域更新后的最终值,所以看起来正常;而打印语句在立即区域执行,拿到的是更新前的旧值,导致顺序颠倒。

解决方法

不推荐依赖#1这种非结构化延迟,以下是三种更规范的方案:

1. 使用接口中的时钟块(推荐)

你已经在接口中定义了driver_cb时钟块,它本身就处理了信号的时序同步——默认output #1意味着驱动信号会在时钟边沿后1ps更新,读取信号时也会自动对齐时序。修改驱动代码,通过时钟块访问信号:

task drive_transfer (input my_frame_c trans);    
    @(my_master_vif.driver_cb iff (my_master_vif.reset));
    my_master_vif.driver_cb.dut_command <= 0;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.driver_cb.dut_command), UVM_LOW)
    
    @(my_master_vif.driver_cb iff (my_master_vif.reset));
    my_master_vif.driver_cb.dut_command <= 1;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.driver_cb.dut_command), UVM_LOW)
    //...
endtask : drive_transfer

时钟块会自动处理时序,确保读取到的是更新后的值,彻底避免竞态。

2. 直接打印赋值变量而非读取信号

既然你已经明确要赋值的是0或1,直接打印变量值而非读取接口信号,从根源上避免竞态:

task drive_transfer (input my_frame_c trans);    
    logic cmd_val;
    
    @(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset));
    cmd_val = 0;
    my_master_vif.dut_command <= cmd_val;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", cmd_val), UVM_LOW)
    
    @(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset));
    cmd_val = 1;
    my_master_vif.dut_command <= cmd_val;
    `uvm_info(get_type_name(), $sformatf("myCmd.start:%h", cmd_val), UVM_LOW)
    //...
endtask : drive_transfer

这种方式最直接,不需要依赖任何时序延迟,完全规避读取信号的竞态问题。

3. 延迟到NBA区域之后读取

如果不想用时钟块,可以在打印前显式等待到时钟边沿后的NBA更新完成,比如等待时钟下降沿:

@(posedge my_master_vif.dut_clock_i iff (my_master_vif.reset));
my_master_vif.dut_command <= 0;
@(negedge my_master_vif.dut_clock_i); // 等待NBA更新完成
`uvm_info(get_type_name(), $sformatf("myCmd.start:%h", my_master_vif.dut_command), UVM_LOW)

总结

非阻塞赋值本身是正确的驱动方式,但uvm_info的执行时机和非阻塞赋值的更新时机不在同一个调度区域,导致读取旧值。优先使用时钟块或者直接打印赋值变量的方式解决,避免使用#1这种依赖具体时间的延迟,保证代码的可移植性和稳定性。

内容的提问来源于stack exchange,提问作者bu-ral

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 00:05:37