文章目录
-
- [1. 概述](#1. 概述)
- [2. 为什么需要Agent组件](#2. 为什么需要Agent组件)
-
- [2.1 封装:创建可复用的接口验证单元](#2.1 封装:创建可复用的接口验证单元)
- [2.2 配置:适配不同的验证场景](#2.2 配置:适配不同的验证场景)
- [3. Agent声明与使用](#3. Agent声明与使用)
- [4. 如何配置和使用Agent](#4. 如何配置和使用Agent)
-
- [4.1 在Test中配置Agent的模式](#4.1 在Test中配置Agent的模式)
- [4.2 在Environment中集成Agent](#4.2 在Environment中集成Agent)
- [5. 一个简单的UART Agent示例](#5. 一个简单的UART Agent示例)
- [6. 总结:Agent的核心三要素](#6. 总结:Agent的核心三要素)
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时,未封装的代码结构如下:
systemverilog
// 未使用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通过封装解决了这个问题。它将驱动、序列、监视这三个组件集成为一个标准的、可复用的单元:
systemverilog
// 使用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)
- 组件 :包含 Driver 、Sequencer 和 Monitor。
- 功能 :能够主动生成并驱动激励信号到设计接口,同时监控接口响应。这是最常用的模式,用于绝大多数主动测试。
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) 完成,实现了测试场景与验证平台结构的解耦:
systemverilog
// 在测试中动态配置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的最基础模板。关键是通过配置决定其工作模式。
systemverilog
// 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接口,而在另一个测试中只需监听一个已有数据流。
systemverilog
// 在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中。
systemverilog
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是什么样子。
systemverilog
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_phase 与 connect_phase |
在build_phase中根据模式创建组件;在connect_phase中连接Driver与Sequencer。 |
Agent在UVM层次中的角色:
- 相对于Test (导演)和Env (摄影棚),Agent是具体的演员班组,负责执行接口层面的具体任务。
- 它是一个标准的、可即插即用的功能模块,极大提高了验证平台搭建的效率和代码的复用性。
掌握Agent的构建和使用,你就掌握了组织UVM验证平台基础功能单元的方法,这是构建复杂且有序的验证系统的基石。