1. 概述

  UVM Agent 是UVM验证框架中负责接口级验证的核心功能单元。你可以把它理解为一个针对特定协议或接口的 “标准化作战小队” 。这个小队有明确分工:有人负责发送激励(driver),有人负责监视接口(monitor),还有一个指挥中心负责调度任务(sequencer)。

  uvm_agent 的主要职责是封装。它将驱动(Driver)、监视(Monitor)和序列调度(Sequencer)这三个与接口交互最紧密的组件打包在一起,对外提供一个统一的、可配置的模块。这样,当我们需要验证一个具有UART、SPI、I2C等多个接口的芯片时,就可以为每个接口实例化一个对应的Agent,使验证平台结构清晰、高度模块化。

2. 为什么需要Agent组件

2.1 封装:创建可复用的接口验证单元

  在芯片验证中,对每个物理接口(如UART、I2C、APB)的验证通常需要三个核心组件协同工作:驱动器(Driver) 负责产生激励,监视器(Monitor) 负责采集信号,序列器(Sequencer) 负责调度事务。如果不进行封装,在验证包含多个相同接口的设计时,代码会迅速变得冗长且难以维护。

  例如,验证一个带有两个UART接口的DUT时,未封装的代码结构如下:

// 未使用Agent:组件分散,代码重复
class messy_env extends uvm_env;
    // UART 1 的独立组件
    uart_driver    drv1;
    uart_sequencer sqr1;
    uart_monitor   mon1;
    // UART 2 的独立组件(与UART 1 几乎完全相同)
    uart_driver    drv2;
    uart_sequencer sqr2;
    uart_monitor   mon2;
    // ... 还需要为每个组件单独配置和连接
endclass

  这种方式导致组件实例化代码重复连接关系复杂,且任何接口协议的改动都需要在多处同步修改,维护成本高。

  UVM Agent通过封装解决了这个问题。它将驱动、序列、监视这三个组件集成为一个标准的、可复用的单元:

// 使用Agent:结构清晰,模块化
class clean_env extends uvm_env;
    // 实例化两个UART Agent,重用同一套组件结构
    uart_agent uart_agent_1;
    uart_agent uart_agent_2;
endclass

  封装带来的核心优势是模块化。一个设计好的Agent(如uart_agent)成为了一个独立的“黑盒”,其内部组件的创建、连接和协议细节被隐藏。在顶层环境中,我们只需将其作为一个整体来实例化和配置,极大地简化了系统集成。

2.2 配置:适配不同的验证场景

  封装保证了Agent作为一个完整单元的可复用性,而可配置性则赋予了它适应不同验证场景的灵活性。这是通过Agent的 is_active 模式开关实现的。

  一个UVM Agent可以工作在两种模式下,其内部组件构成有所不同:

1. 主动模式 (UVM_ACTIVE)

  • 组件:包含 DriverSequencerMonitor
  • 功能:能够主动生成并驱动激励信号到设计接口,同时监控接口响应。这是最常用的模式,用于绝大多数主动测试。

2. 被动模式 (UVM_PASSIVE)

  • 组件:仅包含 Monitor
  • 功能不主动驱动任何信号,仅作为“观察者”监视接口上的活动。常用于:
    • 在系统级验证中,监听某个已有激励的接口。
    • 用于性能收集、覆盖率收集或协议检查,而不干扰原有数据流。

  下图清晰地展示了这两种模式的内部结构差异:

Passive Agent
Active Agent
TLM
Monitor
Sequencer
Driver
Monitor

  这种可配置性使得同一个uart_agent可以在不同测试中扮演不同角色。例如,在模块级测试中,我们将其配置为UVM_ACTIVE来主动发起UART通信;在系统级测试中,同一个UART接口可能由CPU驱动,我们只需将该Agent配置为UVM_PASSIVE来被动监控数据,验证其是否正确传递。

  模式的选择通常在Test层通过UVM配置机制 (uvm_config_db) 完成,实现了测试场景与验证平台结构的解耦:

// 在测试中动态配置Agent模式
class my_test extends uvm_test;
    function void build_phase(uvm_phase phase);
        // 配置为主动模式,进行主动测试
        uvm_config_db#(uvm_active_passive_enum)::set(this, “env.uart_agent0”, “is_active”, UVM_ACTIVE);
        // 配置为被动模式,仅用于监控
        uvm_config_db#(uvm_active_passive_enum)::set(this, “env.uart_agent1”, “is_active”, UVM_PASSIVE);
    endfunction
endclass

3. Agent声明与使用

  以下是构建一个UVM Agent的最基础模板。关键是通过配置决定其工作模式。

// 1. 定义Agent类,必须继承自 uvm_agent
class my_agent extends uvm_agent;
    // 2. 使用宏注册
    `uvm_component_utils(my_agent)

    // 3. 声明内部组件句柄
    my_driver      driver;
    my_sequencer   sequencer;
    my_monitor     monitor;

    // 4. 声明一个配置变量,用于控制Agent是主动(ACTIVE)还是被动(PASSIVE)
    //    uvm_active_passive_enum 是UVM内置的枚举类型,值为 UVM_ACTIVE 或 UVM_PASSIVE
    uvm_active_passive_enum is_active = UVM_ACTIVE;

    // 5. 构造函数
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    // 6. 【核心步骤1】build_phase:根据配置创建组件
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        // 首先创建Monitor,因为无论主动还是被动模式都需要它
        monitor = my_monitor::type_id::create(“monitor”, this);

        // 判断是否为主动模式,如果是则创建Driver和Sequencer
        if (is_active == UVM_ACTIVE) begin
            driver    = my_driver::type_id::create(“driver”, this);
            sequencer = my_sequencer::type_id::create(“sequencer”, this);
        end
    endfunction

    // 7. 【核心步骤2】connect_phase:连接内部组件
    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        // 在主动模式下,需要将Driver的端口连接到Sequencer的出口
        if (is_active == UVM_ACTIVE) begin
            driver.seq_item_port.connect(sequencer.seq_item_export);
        end
        // Monitor通常独立工作,无需在此连接
    endfunction
endclass

4. 如何配置和使用Agent

4.1 在Test中配置Agent的模式

  Agent的灵活性体现在其工作模式可通过上层的Test进行配置。例如,在某个测试中我们可能需要主动驱动UART接口,而在另一个测试中只需监听一个已有数据流。

// 在uvm_test的build_phase中配置Agent
class my_test extends uvm_test;
    my_env env;

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        // 使用配置数据库(set)来设置Agent的 is_active 变量
        // 语法:set( 设置者, “目标组件路径”, “变量名”, 值 )
        uvm_config_db#(uvm_active_passive_enum)::set(this, “env.my_agent_inst”, “is_active”, UVM_ACTIVE);
        // 或者设置为被动模式:
        // uvm_config_db#(uvm_active_passive_enum)::set(this, “env.my_agent_inst”, “is_active”, UVM_PASSIVE);

        env = my_env::type_id::create(“env”, this);
    endfunction
endclass

4.2 在Environment中集成Agent

  Agent作为标准模块,被实例化在Environment中。

class my_env extends uvm_env;
    `uvm_component_utils(my_env)
    my_agent agent; // 声明Agent

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        agent = my_agent::type_id::create(“agent”, this); // 创建Agent
        // Agent内部的模式已在Test中配置,此处无需再指定
    endfunction
    // ... connect_phase 等
endclass

5. 一个简单的UART Agent示例

  让我们将上述模板具体化,看看一个用于UART接口的Agent是什么样子。

class uart_agent extends uvm_agent;
    `uvm_component_utils(uart_agent)

    uart_driver    driver;
    uart_sequencer sequencer;
    uart_monitor   monitor;
    uart_config    cfg; // 假设有一个配置对象,包含波特率等参数
    uvm_active_passive_enum is_active = UVM_ACTIVE;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        // 获取从上层传递下来的配置对象
        if(!uvm_config_db#(uart_config)::get(this, “”, “cfg”, cfg)) begin
            `uvm_warning(“CFG”, “未找到配置对象,使用默认值”)
            cfg = uart_config::type_id::create(“cfg”);
        end

        monitor = uart_monitor::type_id::create(“monitor”, this);
        // 将配置传递给Monitor
        uvm_config_db#(uart_config)::set(this, “monitor”, “cfg”, cfg);

        if (is_active == UVM_ACTIVE) begin
            driver = uart_driver::type_id::create(“driver”, this);
            sequencer = uart_sequencer::type_id::create(“sequencer”, this);
            // 将配置传递给Driver
            uvm_config_db#(uart_config)::set(this, “driver”, “cfg”, cfg);
        end
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        if (is_active == UVM_ACTIVE) begin
            driver.seq_item_port.connect(sequencer.seq_item_export);
        end
    endfunction
endclass

6. 总结:Agent的核心三要素

  理解uvm_agent,请抓住以下三个核心要点:

要素 说明 关键点
1. 核心成员 Driver, Sequencer, Monitor 构成接口验证的“铁三角”。Driver和Sequencer在主动模式下才存在。
2. 工作模式 Active(主动) / Passive(被动) 通过 is_active 变量控制。这是Agent灵活性的关键。
3. 核心方法 build_phaseconnect_phase build_phase中根据模式创建组件;在connect_phase中连接Driver与Sequencer。

  Agent在UVM层次中的角色

  • 相对于Test(导演)和Env(摄影棚),Agent是具体的演员班组,负责执行接口层面的具体任务。
  • 它是一个标准的、可即插即用的功能模块,极大提高了验证平台搭建的效率和代码的复用性。

  掌握Agent的构建和使用,你就掌握了组织UVM验证平台基础功能单元的方法,这是构建复杂且有序的验证系统的基石。


上一篇:UVM验证入门(14)-coverage覆盖率收集
下一篇:UVM验证入门(16)-uvm_env验证环境容器

参考文档:UVM_Class_Reference_Manual_1.0.pdf

Logo

更多推荐