APB协议 构建一个完整的 UVM验证VIP Agent介绍类的要素
本文详细介绍了基于APB协议构建UVM验证VIP Agent的完整设计方法。重点阐述了三大核心组件:Transaction类用于封装APB协议数据(地址、数据、读写标志),支持随机化生成多样化测试场景;Sequence类定义激励生成逻辑,控制测试策略;Sequencer类协调事务调度与传递。每个组件均包含工作原理说明和带注释的代码实现,展示了如何通过UVM框架实现验证环境的标准化构建。该设计支持随
下面将基于 APB协议 构建一个完整的 UVM验证VIP Agent,包含 Transaction(事务类)、Sequence(序列类)、Sequencer(序列发生器类) 三大核心组件,并扩展 Driver(驱动类)、Monitor(监测类)、**Agent(代理类)** 形成完整验证环境。每个组件均附带 工作原理详解 和 逐行注释,确保彻底理解其设计逻辑和UVM协作机制。
完整APB验证VIP Agent设计
1. Transaction(事务类)—— 描述APB激励的数据结构
工作原理
Transaction是验证平台中最基础的单元,用于 描述DUT(被测设计)的一次输入或输出行为。在APB协议中,一个事务包含 地址(addr)、数据(data)、**读写标志(we)** 等关键信息。
核心作用:作为验证激励的载体,通过随机化生成多样化的测试场景(如不同地址、数据组合),驱动DUT并验证其功能正确性。
代码实现(apb_transaction.sv)
// 定义APB事务类,继承自uvm_sequence_item(UVM标准基类,支持序列化、打印、随机化等)
class apb_transaction extends uvm_sequence_item;
// ===== 成员变量:描述APB事务的属性(数据) =====
// rand logic [3:0] addr; // 4位地址信号(随机化,APB协议中地址宽度通常为4-32bit,此处简化)
// rand logic [7:0] data; // 8位数据信号(随机化,APB数据宽度简化为8bit)
// rand bit we; // 写使能标志(1=写操作,0=读操作)
// 注意:原案例中为演示封装改为local,此处恢复rand以支持随机化(验证必需)
rand logic [3:0] addr; // 4位地址(随机化,覆盖0~15)
rand logic [7:0] data; // 8位数据(随机化,覆盖0~255)
rand bit we; // 写使能(1=写DUT,0=读DUT)
// ===== UVM支持宏:注册成员变量到UVM框架(支持打印、随机化、约束等) =====
`uvm_object_utils_begin(apb_transaction)
`uvm_field_int(addr, UVM_DEFAULT) // 注册addr变量(支持UVM打印和随机化)
`uvm_field_int(data, UVM_DEFAULT) // 注册data变量
`uvm_field_int(we, UVM_DEFAULT) // 注册we变量
`uvm_object_utils_end
// ===== 构造函数:初始化对象(必须调用父类构造函数) =====
// 参数name:对象的名称(用于调试时标识,可选,默认"apb_transaction")
function new(string name="apb_transaction");
super.new(name); // 调用父类uvm_sequence_item的构造函数,初始化UVM内部状态
endfunction
// ===== 可选:约束随机化范围(限制地址和数据的有效范围) =====
// 作用:确保生成的地址和数据符合APB协议规范(例如地址0x0~0xF,数据0x00~0xFF)
constraint addr_range { addr inside {[0:15]}; } // 地址范围:4bit有效(0~15)
constraint data_range { data inside {[0:255]}; } // 数据范围:8bit有效(0~255)
// ===== 可选:成员方法(打印事务内容,调试用) =====
// 功能:将事务的addr/data/we值格式化输出到仿真控制台
function void print_transaction();
$display("APB Transaction: addr=0x%h, data=0x%h, we=%0b (操作=%s)",
addr, data, we, (we ? "写" : "读"));
endfunction
endclass
关键点总结:
•随机化支持:rand关键字允许通过约束(如 addr_range)生成符合协议的随机地址/数据(覆盖更多测试场景)。
•UVM集成:uvm_object_utils宏让UVM能识别该类(支持序列化、打印、随机化约束)。
•调试辅助:print_transaction()方法用于验证时检查事务内容是否符合预期。
2. Sequence(序列类)—— 生成激励事务的逻辑
工作原理
Sequence是UVM中 生成激励事务(Transaction)的逻辑单元,通过定义 body()任务来创建一系列Transaction,并通过Sequencer发送给Driver。
核心作用:控制激励的生成策略(如顺序执行、随机生成、特定场景覆盖),是验证平台中 “测试用例的源头”。
**代码实现(apb_sequence.sv)**
// 定义APB序列类,继承自uvm_sequence<apb_transaction>(专用于生成apb_transaction类型的事务)
class apb_sequence extends uvm_sequence #(apb_transaction);
// ===== 构造函数:初始化序列(必须调用父类构造函数) =====
// 参数name:序列名称(用于调试标识,可选,默认"apb_sequence")
function new(string name="apb_sequence");
super.new(name); // 调用父类uvm_sequence的构造函数
endfunction
// ===== 核心方法:定义激励生成逻辑(UVM自动调用) =====
// 作用:在仿真时,Sequencer会调用此任务的body()来生成一系列apb_transaction并发送给Driver
task body();
// 示例1:生成10个随机APB事务(覆盖不同地址和读写操作)
repeat(10) begin
// 1. 创建一个新的APB事务对象(通过工厂创建,支持UVM的随机化和约束)
apb_transaction txn = apb_transaction::type_id::create("txn");
// 2. 随机化事务(根据类中定义的约束生成随机addr/data/we)
if (!txn.randomize()) begin
`uvm_error("RAND_FAIL", "APB事务随机化失败!") // 随机化失败时报错
end
// 3. 打印生成的事务内容(调试用)
`uvm_info("SEQ_GEN", $sformatf("生成事务: %s", txn.sprint()), UVM_LOW)
txn.print_transaction(); // 调用事务的打印方法
// 4. 将事务发送给Sequencer(通过UVM的put/get机制,Driver会从Sequencer获取)
start_item(txn); // 通知Sequencer准备接收事务
finish_item(txn); // 将事务传递给Sequencer(实际驱动Driver)
end
// 示例2(可选):生成一个特定的固定事务(例如地址0x0,写数据0xAA)
// apb_transaction fixed_txn = apb_transaction::type_id::create("fixed_txn");
// fixed_txn.addr = 4'h0;
// fixed_txn.data = 8'hAA;
// fixed_txn.we = 1;
// if (!fixed_txn.randomize()) `uvm_error("RAND_FAIL", "固定事务随机化失败");
// start_item(fixed_txn);
// finish_item(fixed_txn);
endtask
endclass
关键点总结:
•随机化激励:通过 txn.randomize()生成符合约束(如 addr_range)的随机事务,覆盖更多测试场景。
•UVM流程:start_item(txn)和 finish_item(txn)是UVM的标准方法,用于将事务传递给Sequencer(底层通过TLM通信)。
•调试信息:uvm_info和 print_transaction()帮助验证人员观察生成的激励是否符合预期。
3. Sequencer(序列发生器类)—— 协调事务的调度与传递
工作原理
Sequencer是UVM中 管理Sequence和Driver之间事务传递的中间组件,负责从Sequence接收生成的事务(Transaction),并通过TLM端口(如 seq_item_port)将事务分发给Driver。
核心作用:作为 “事务调度中心”,确保Driver能按顺序获取Sequence生成的激励,并支持多Sequence并发(通过分层序列)。
代码实现(apb_sequencer.sv)
// 定义APB Sequencer类,继承自uvm_sequencer<apb_transaction>(专用于处理apb_transaction类型)
class apb_sequencer extends uvm_sequencer #(apb_transaction);
// ===== 构造函数:初始化Sequencer(必须调用父类构造函数) =====
// 参数name:Sequencer名称(用于调试标识,可选,默认"apb_sequencer")
// 参数parent:父组件(UVM层次化结构,通常为agent)
function new(string name="apb_sequencer", uvm_component parent);
super.new(name, parent); // 调用父类uvm_sequencer的构造函数,初始化TLM端口和内部状态
endfunction
endclass
关键点总结:
•TLM通信:Sequencer内部预定义了 seq_item_port(发送事务给Driver)和 seq_item_export(接收Sequence的事务),无需手动实现。
•泛型参数:uvm_sequencer #(apb_transaction)指定该Sequencer仅处理 apb_transaction类型的事务(类型安全)。
•层次化支持:通过构造函数的 parent参数,Sequencer可嵌入到Agent的层次结构中(例如 apb_agent包含 apb_sequencer)。
4. Driver(驱动类)—— 将事务转为DUT的时序信号
工作原理
Driver是UVM中 **将Transaction(抽象激励)转换为DUT的实际输入信号(时序逻辑)** 的组件。它通过Sequencer获取事务,并按照协议规范(如APB的时序)驱动DUT的引脚信号(如PADDR、PWDATA、PWRITE)。
核心作用:作为 “协议转换器”,确保验证平台生成的激励能被DUT正确识别和处理。
代码实现(apb_driver.sv)
// 定义APB Driver类,继承自uvm_driver<apb_transaction>(专用于驱动apb_transaction类型)
class apb_driver extends uvm_driver #(apb_transaction);
// ===== 虚接口:连接到DUT的APB信号(通过virtual interface传递) =====
// 作用:封装DUT的物理引脚信号(如PCLK时钟、PADDR地址、PWDATA数据等)
// virtual关键字表示这是一个指针,指向实际的APB接口实例(避免硬编码连接)
virtual apb_interface vif;
// ===== 构造函数:初始化Driver(必须调用父类构造函数) =====
// 参数name:Driver名称(用于调试标识,可选,默认"apb_driver")
// 参数parent:父组件(通常为agent)
function new(string name="apb_driver", uvm_component parent);
super.new(name, parent); // 调用父类uvm_driver的构造函数,初始化内部状态
endfunction
// ===== 核心方法:从Sequencer获取事务并驱动DUT时序 =====
// 作用:UVM自动调用此任务,循环获取Transaction并转换为DUT的APB时序信号
task run_phase(uvm_phase phase);
// 1. 永久循环(持续等待并处理事务,直到仿真结束)
forever begin
// 2. 从Sequencer获取一个apb_transaction(阻塞等待,直到Sequence生成事务)
apb_transaction txn;
seq_item_port.get_next_item(txn); // 阻塞调用,获取下一个事务
// 3. 调用方法:将事务转为DUT的APB时序信号
drive_apb_signal(txn);
// 4. 通知Sequencer当前事务已完成(允许Sequence生成下一个事务)
seq_item_port.item_done();
end
endtask
// ===== 辅助方法:根据事务驱动APB时序(具体协议实现) =====
// 作用:按照APB协议规范,将txn的addr/data/we转换为DUT的物理信号
// 参数:txn 是当前处理的APB事务对象
task drive_apb_signal(apb_transaction txn);
// 1. 准备阶段:拉高片选(PSEL),设置地址和数据
vif.psel <= 1'b1; // 片选有效(告诉DUT:我要访问你)
vif.paddr <= txn.addr; // 设置要访问的地址(来自事务的addr)
vif.pwdata <= txn.data; // 设置要写入的数据(如果是写操作)
vif.pwrite <= txn.we; // 设置读写标志(1=写,0=读)
// 2. 等待时钟上升沿(APB协议通常在时钟边沿采样信号)
@(posedge vif.pclk); // 等待DUT的时钟上升沿(确保信号稳定)
// 3. 使能传输:拉高PENABLE(开始传输)
vif.penable <= 1'b1; // 传输使能(告诉DUT:现在开始操作)
// 4. 再次等待时钟上升沿(确保DUT完成操作)
@(posedge vif.pclk);
// 5. 结束传输:拉低PENABLE和PSEL(释放总线)
vif.penable <= 1'b0; // 结束传输
vif.psel <= 1'b0; // 释放片选
endtask
endclass
关键点总结:
•**虚接口(vif)**:通过 virtual apb_interface传递DUT的实际信号(如 paddr、pwdata),避免硬编码连接(提高代码可移植性)。
•TLM通信:seq_item_port.get_next_item(txn)从Sequencer获取事务(阻塞式,确保Driver按顺序处理)。
•协议实现:drive_apb_signal()方法封装了APB的时序逻辑(如PSEL→PADDR→PENABLE的时序步骤),确保DUT正确响应。
5. Monitor(监测类)—— 捕获DUT的输出并验证
工作原理
Monitor是UVM中 监测DUT的输出信号(如读数据PRDATA),并将其转换为Transaction对象上报给Scoreboard/Checker 的组件。它被动监听DUT的引脚信号,不主动驱动DUT。
核心作用:作为 “观测者”,确保DUT的输出符合预期(例如读操作返回正确的数据)。
代码实现(apb_monitor.sv)
// 定义APB Monitor类,继承自uvm_monitor(UVM标准监控基类)
class apb_monitor extends uvm_monitor;
// ===== 虚接口:连接到DUT的APB信号(用于监测输入输出) =====
virtual apb_interface vif;
// ===== UVM分析端口:将捕获的Transaction上报给Scoreboard/Checker =====
// 作用:通过此端口将生成的apb_transaction发送给其他组件(如Scoreboard)
uvm_analysis_port #(apb_transaction) ap;
// ===== 构造函数:初始化Monitor(必须调用父类构造函数) =====
function new(string name="apb_monitor", uvm_component parent);
super.new(name, parent); // 调用父类uvm_monitor的构造函数
ap = new("ap", this); // 创建分析端口(名称"ap",关联当前Monitor实例)
endfunction
// ===== 核心方法:监测DUT信号并生成Transaction(运行在run_phase) =====
task run_phase(uvm_phase phase);
// 1. 永久循环(持续监测DUT信号,直到仿真结束)
forever begin
// 2. 等待DUT的读操作(示例:监测PREADY和PRDATA信号,简化逻辑)
// 注意:实际APB协议需监测PSEL、PENABLE、PREADY等信号组合
@(posedge vif.pclk); // 等待时钟上升沿
// 3. 检测读操作(示例条件:PSEL高且PENABLE高且PWRITE=0表示读操作)
if (vif.psel && vif.penable && !vif.pwrite) begin
// 4. 创建一个新的APB事务对象(描述读操作)
apb_transaction txn = apb_transaction::type_id::create("monitored_txn");
// 5. 填充事务属性(从DUT信号获取地址和数据)
txn.addr = vif.paddr; // 读取的地址(来自DUT的PADDR信号)
txn.data = vif.prdata; // 读取的数据(来自DUT的PRDATA信号)
txn.we = 0; // 读操作标志
// 6. 打印监测到的事务(调试用)
`uvm_info("MONITOR", $sformatf("监测到读事务: %s", txn.sprint()), UVM_LOW)
txn.print_transaction();
// 7. 通过分析端口上报事务(Scoreboard将接收并验证)
ap.write(txn);
end
end
endtask
endclass
关键点总结:
•被动监测:Monitor不主动驱动DUT,仅监听DUT的引脚信号(如 paddr、prdata)。
•**分析端口(ap)**:通过 uvm_analysis_port将监测到的Transaction发送给Scoreboard(或其他验证组件),实现数据比对。
•协议解析:根据APB信号(如 psel、penable、pwrite)判断当前是读操作还是写操作,并提取对应的地址/数据。
6. Agent(代理类)—— 整合Sequencer、Driver和Monitor
工作原理
Agent是UVM中 封装Sequencer、Driver和Monitor的容器,用于管理一个DUT接口(如APB接口)的完整验证环境。它根据配置(如主动/被动模式)决定是否包含Driver(主动模式需要驱动DUT,被动模式仅监测)。
核心作用:作为 “验证环境的模块化单元”,简化顶层环境的集成(例如一个芯片可能包含APB、I2C等多个Agent)。
代码实现(apb_agent.sv)
// 定义APB Agent类,继承自uvm_agent(UVM标准代理基类)
class apb_agent extends uvm_agent;
// ===== 组件实例:Sequencer、Driver、Monitor =====
apb_sequencer sqr; // 序列发生器(生成激励)
apb_driver drv; // 驱动器(将激励转为DUT信号)
apb_monitor mon; // 监测器(捕获DUT输出)
// ===== 构造函数:初始化Agent(必须调用父类构造函数) =====
function new(string name="apb_agent", uvm_component parent);
super.new(name, parent); // 调用父类uvm_agent的构造函数
endfunction
// ===== 核心方法:构建Agent的组件(UVM的build_phase) =====
// 作用:在仿真初始化阶段创建Sequencer、Driver和Monitor实例
function void build_phase(uvm_phase phase);
super.build_phase(phase); // 调用父类build_phase(可选,但推荐)
// 创建Sequencer(处理apb_transaction类型)
sqr = apb_sequencer::type_id::create("sqr", this);
// 创建Driver(驱动APB事务)
drv = apb_driver::type_id::create("drv", this);
// 创建Monitor(监测APB事务)
mon = apb_monitor::type_id::create("mon", this);
endfunction
// ===== 核心方法:连接Agent的组件(UVM的connect_phase) =====
// 作用:在组件创建后,建立Sequencer与Driver之间的TLM通信连接
function void connect_phase(uvm_phase phase);
super.connect_phase(phase); // 调用父类connect_phase(可选)
// 将Driver的seq_item_port连接到Sequencer的seq_item_export(事务传递通道)
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
关键点总结:
•层次化构建:在 build_phase中创建Sequencer、Driver和Monitor实例(通过 type_id::create方法,支持UVM工厂机制)。
•TLM连接:在 connect_phase中通过 drv.seq_item_port.connect(sqr.seq_item_export)建立Sequencer与Driver的通信通道(Driver从Sequencer获取事务)。
**•主动/被动模式:**当前Agent为 主动模式(包含Driver,用于驱动DUT),若为被动模式(仅监测),可省略Driver实例。
完整VIP Agent的顶层集成(示例)
代码实现(apb_env.sv,可选扩展)
// 定义APB验证环境(集成Agent、Scoreboard等,此处仅展示Agent集成)
class apb_env extends uvm_env;
apb_agent apb_agt; // APB Agent实例(包含Sequencer/Driver/Monitor)
function new(string name="apb_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 创建APB Agent(主动模式,驱动并监测APB接口)
apb_agt = apb_agent::type_id::create("apb_agt", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// 可在此处连接Scoreboard(例如:apb_agt.mon.ap.connect(scoreboard.ap_port))
endfunction
endclass
总结:各组件协作流程
1.Transaction:描述APB激励(地址/数据/读写标志),通过随机化生成多样化测试场景。
2.Sequence:定义激励生成逻辑(如循环生成10个随机事务),通过 body()任务创建Transaction并交给Sequencer。
3.Sequencer:接收Sequence的事务,通过TLM端口(seq_item_port)分发给Driver。
4.Driver:从Sequencer获取Transaction,按照APB协议时序驱动DUT的物理信号(如PADDR、PWDATA)。
5.Monitor:被动监听DUT的输出信号(如PRDATA),转换为Transaction并通过分析端口上报给Scoreboard。
6.Agent:整合Sequencer、Driver和Monitor,形成完整的DUT接口验证环境。
通过以上组件的协作,UVM验证平台能够 自动生成激励、驱动DUT、监测输出并验证功能正确性,是芯片验证的核心基础设施。

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。
更多推荐
所有评论(0)