UVM验证入门(7)-uvm_driver信号驱动器

7 阅读7分钟

1. 概述

  在基于UVM(Universal Verification Methodology)的验证平台上,Driver(驱动器)是一个至关重要的动态组件。它扮演着“执行者”的角色,专门负责将事务级(Transaction-Level)的激励数据,按照目标接口的物理时序协议,转换为引脚级的信号驱动。简单来说,Driver是连接验证平台抽象事务与待测设计(DUT)具体物理接口的桥梁。没有Driver,抽象的测试场景就无法施加到DUT上。

1.1 数据流转换中心

// Driver在UVM数据流中的位置
Sequence Item (抽象事务) → uvm_driver → DUT Signals (具体波形)

1.2 主要职责

  • 事务获取:从sequencer获取序列项
  • 协议转换:将事务转换为DUT接口时序
  • 信号驱动:按照协议要求驱动物理信号
  • 响应处理:可选地向sequencer返回响应

  将uvm部分验证环境看成一个餐厅出菜系统,方便我们理解transaction、sequence、sequencer、driver、dut的关系。

image.png

2. 只有driver的uvm验证环境

2.1 搭建环境

  如下图搭建了一个只有driver的UART验证环境,将uart的发送与接收回环,根据发送的数据与接收的数据对比,判断数据是否正常:

my_driver的实现如下,直接在driver中驱动信号:

`ifndef MY_DRIVER_SV
`define MY_DRIVER_SV

`include "uvm_macros.svh"	//uvm宏定义文件
import uvm_pkg::*;				//导入uvm包
class my_driver extends uvm_driver;	//继承自uvm_driver
    `uvm_component_utils(my_driver)	//注册到工厂

    function new(string name="my_driver",uvm_component parent = null);
        super.new(name,parent);
    endfunction

    function void end_of_elaboration_phase (uvm_phase phase);
        uvm_root::get().print_topology();	//在end_of_elaboration_phase 打印uvm验证环境拓扑
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);		//objection机制
        while(!top_tb.rst_n)					//等待复位完成
            @(posedge top_tb.clk);
        `uvm_info("my_driver","system reset finish!!!",UVM_LOW)
        for(int i=0; i<10; i++)begin
            wait(top_tb.tx_busy==1'b0);
            top_tb.tx_en <= 1'b1;				//发送数据
            top_tb.tx_data <= $urandom_range(0,255);
            #10;
            top_tb.tx_en <= 1'b0;
            wait(top_tb.tx_done);				//等待发送完成,打印发送数据
            `uvm_info("my_driver",$sformatf("uart tx_data is 0x%0X",top_tb.tx_data),UVM_LOW)

            wait(top_tb.rx_valid);				//等待接收数据有效,打印接收到的数据
            `uvm_info("my_driver",$sformatf("uart rx_data is 0x%0X",top_tb.rx_data),UVM_LOW)
            #10;
         end
         phase.drop_objection(this);
    endtask

endclass

`endif

再在顶层testbenc中例化dut(uart_top):

module top_tb;

import uvm_pkg::*;
`include "uvm_macros.svh"

reg clk, rst_n;
reg tx_en;
reg [7:0] tx_data;
wire tx_busy, tx_done, tx_out;

wire rx_in;
wire rx_valid;
wire [7:0] rx_data;
wire rx_error, rx_busy;


// UART实例化
uart_top #(
    .CLK_FREQ(100_000_000),
    .BAUD_RATE(115200),
    .DATA_BITS(8),
    .STOP_BITS(1),
    .PARITY(0)
) dut (
    .clk(clk),
    .rst_n(rst_n),
    .tx_en(tx_en),
    .tx_data(tx_data),
    .tx_busy(tx_busy),
    .tx_done(tx_done),
    .tx_out(tx_out),
    .rx_in(rx_in),
    .rx_valid(rx_valid),
    .rx_data(rx_data),
    .rx_error(rx_error),
    .rx_busy(rx_busy)
);

assign rx_in = tx_out;

// 时钟生成
initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 100MHz时钟
end

initial begin
    // 初始化
    rst_n = 0;
    tx_en = 0;
    tx_data = 8'h00;
    // 复位
    #100 rst_n = 1;
end

initial begin

    run_test("my_driver");	//启动uvm

end

initial begin
    $fsdbDumpfile("wave.fsdb");	//dump fsdb波形
    $fsdbDumpvars(0,top_tb);
end

endmodule

仿真结果如下图:

仿真环境下载:一个只有driver的UART简单验证环境

2.2 当前环境的显著劣势

  虽然上述环境能运行,但这与verilog中的仿真基本没有任何区别,并不能体现uvm环境的优势。 全局路径硬编码 - 可重用性差

// 问题代码:直接引用顶层模块信号
wait(top_tb.tx_busy==1'b0);
top_tb.tx_en <= 1'b1;
top_tb.tx_data <= $urandom_range(0,255);
  • Driver与特定的测试平台绑定,无法在其他项目中重用
  • 修改顶层模块名称或信号名称需要修改Driver代码
  • 违反了UVM的封装原则

激励生成与驱动逻辑耦合

// 问题代码:激励生成和驱动逻辑混合在一起
top_tb.tx_data <= $urandom_range(0,255);  // 激励生成
top_tb.tx_en <= 1'b1;                     // 驱动逻辑
  • 无法实现激励的重用和组合
  • 难以创建复杂的测试场景
  • 无法实现约束随机验证

缺乏配置机制

// 问题:没有配置接口,所有参数硬编码
for(int i=0; i<10; i++)begin  // 固定10次传输
  • 无法在测试用例中动态配置Driver行为
  • 无法实现不同测试用例的不同配置需求
  • 调试和验证灵活性受限

同步机制原始

// 问题:使用原始的时序等待
#10;
wait(top_tb.tx_done);
  • 时序控制不精确,容易产生竞争条件
  • 无法适应不同时钟频率的配置
  • 代码可读性和维护性差

3. 通用Driver实现

3.1 最简单的Driver示例

class basic_driver extends uvm_driver #(basic_transaction);
    `uvm_component_utils(basic_driver)
    virtual interface dut_if vif; //定义虚拟接口,用于与dut的连接
    
    virtual task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req); 	//使用get_next_item方法获取一个事务
            drive_transaction(req); 			//自定义方法,驱动dut					
            seq_item_port.item_done();			//item_done方法告知sequence,当前事务已经处理完成
        end
    endtask
endclass

  通用driver事务来源一般为sequencer,seq_itme_port是driver与sequencer通信的TLM prot。关于TLM可查看UVM验证入门(4)-TLM1.0)

这个最简单的Driver示例体现了UVM驱动器的三个基本职责:

  • 通信机制:通过seq_item_port与Sequencer建立标准TLM通信通道
  • 事务获取:使用get_next_item(req)从Sequencer获取激励事务
  • 完成确认:通过item_done()通知Sequencer当前事务处理完成

工作流程详解:

  • get_next_item(req):阻塞方法,等待Sequencer发送下一个事务对象
  • drive_transaction(req):自定义的驱动方法,将事务级数据转换为信号级时序
  • item_done():关键的回调方法,允许Sequencer发送下一个事务

3.2 虚拟接口连接

virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // 获取虚拟接口
    if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif))
        `uvm_fatal("NOVIF", "Virtual interface not set")
endfunction

配置数据库机制:   虚拟接口连接是UVM验证环境与DUT物理接口的桥梁,通过UVM配置数据库实现:

  • 接口设置端(通常在测试平台顶层):
// 在top_tb中将物理接口注册到config_db
uvm_config_db#(virtual dut_if)::set(null, "uvm_test_top.env.agent.driver", "vif", dut_if_inst);
  • 接口获取端(在Driver的build_phase中):
// 使用相同的路径和标识符获取接口
uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif);

4. Driver与Sequencer的通信机制

4.1 端口连接

class test_env extends uvm_env;
    virtual function void connect_phase(uvm_phase phase);
        // Driver与Sequencer的关键连接
        driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction
endclass

TLM端口连接机制:

  这是UVM组件间通信的核心基础设施,建立了Driver与Sequencer之间的标准通信通道:

  • seq_item_port:Driver的TLM拉取端口,主动请求事务
  • seq_item_export:Sequencer的TLM导出接口,提供事务流控制

连接时机与位置:

  • 在connect_phase中执行,确保所有组件已完成实例化
  • 通常在环境的connect_phase中完成此关键连接
  • 遵循UVM的phase执行顺序:build_phase → connect_phase → run_phase

通信模式:

  • 采用拉取(pull)模式,Driver主动从Sequencer获取事务
  • Sequencer负责事务的仲裁、选择和流控制
  • 支持复杂的序列调度和优先级管理

4.2 完整的通信流程

virtual task get_and_drive();
    forever begin
        // 1. 请求下一个事务
        seq_item_port.get_next_item(req);
        
        // 2. 处理事务
        process_transaction(req);
        
        // 3. 可选:返回响应
        if (req.need_response) begin
            rsp = advanced_transaction::type_id::create("rsp");
            rsp.status = SUCCESS;
            seq_item_port.put_response(rsp);
        end
        
        // 4. 标记事务完成
        seq_item_port.item_done();
    end
endtask

事务请求阶段

  • get_next_item(req):阻塞调用,等待Sequencer提供有效事务
  • 在此期间,Sequencer可以执行序列调度、仲裁和优先级处理

事务处理阶段

  • process_transaction(req):将事务级数据转换为信号级时序
  • 实现具体的协议驱动逻辑,如握手、时序控制等

响应返回阶段(可选)

  • 创建响应对象并填充状态信息
  • 通过put_response(rsp)将响应返回给原始序列
  • 支持请求-响应通信模式

完成确认阶段

  • item_done():关键的非阻塞调用,通知Sequencer可以发送下一事务
  • 必须在事务处理完成后调用,否则会阻塞序列执行

5. 高级Driver特性

5.1 复位处理机制

  在真实的验证环境中,复位处理是确保验证可靠性的关键特性。正确的复位处理能够:

  • 避免复位期间的竞态条件
  • 确保复位后信号和内部状态正确初始化
  • 防止事务丢失或重复处理
class simple_driver extends uvm_driver;
    `uvm_component_utils(simple_driver)
    virtual dut_if vif;
    
    virtual task run_phase(uvm_phase phase);
        // 等待复位完成
        wait(!vif.rst_n);
        
        // 开始驱动事务
        forever begin
            seq_item_port.get_next_item(req);
            drive_item(req);
            seq_item_port.item_done();
        end
    endtask
    
    virtual task drive_item(uvm_sequence_item item);
        // 简单的驱动逻辑
        @(posedge vif.clk);
        vif.data <= item.data;
        vif.valid <= 1'b1;
        @(posedge vif.clk);
        vif.valid <= 1'b0;
    endtask
endclass

5.2 可配置Driver

  可配置性是UVM验证环境的重要特性,它使得同一个Driver组件能够适应不同的测试需求和工作模式,大大增强了代码的可重用性和验证的灵活性。

  • 参数化行为:通过外部配置控制Driver的内部行为,如时序延迟、工作模式等
  • 测试场景适配:不同的测试用例可以配置不同的Driver参数,无需修改Driver代码
  • 调试便利性:通过调整配置参数快速定位问题,提高调试效率
class config_driver extends uvm_driver;
    `uvm_component_utils(config_driver)
    
    int delay = 1;
    
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        // 从配置获取参数
        uvm_config_db#(int)::get(this, "", "delay", delay);
    endfunction
    
    virtual task drive_item(uvm_sequence_item item);
        repeat(delay) @(posedge vif.clk);
        vif.data <= item.data;
        vif.valid <= 1'b1;
        @(posedge vif.clk);
        vif.valid <= 1'b0;
    endtask
endclass

//在test配置
class my_test extends uvm_test;
    `uvm_component_utils(my_test)
    
    virtual function void build_phase(uvm_phase phase);
        // 设置driver延迟
        uvm_config_db#(int)::set(this, "env.driver", "delay", 5);
    endfunction
endclass

6. 总结

  UVM Driver作为验证平台的核心组件,承担着事务级到信号级转换的关键桥梁作用。从简单的信号驱动器到具备复位处理、动态配置等高级特性的智能组件,Driver的设计质量直接影响验证环境的可重用性和效率。掌握Driver的通信机制、配置方法和高级特性,是构建标准化、可扩展UVM验证环境的基础,为复杂SoC验证提供坚实保障。

参考文档:UVM_Cl ass_Reference_Manual_1.0.pdf