news 2026/2/28 7:41:03

19.1 UVM Phase流程详解?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
19.1 UVM Phase流程详解?


🎯 一、两张图的关系:整体 vs 局部

图1=图2中run_phase的"放大镜"

图2 (总览)对应的图1 (详细分解)
run_phase(一个大盒子)拆成12个小phase:pre_resetresetpost_resetpre_configure→ … →post_shutdown

这就像是:

  • 图2“学校一天的作息时间表”
  • 图1“上午8:00-11:30的语文课上,老师具体怎么安排45分钟”

💡 二、为什么需要这么多Phase?

生活问题:如果让你"一天只做一件事"会怎样?

  • 没法边吃饭边睡觉(功能冲突)
  • 没法先写作业再起床(顺序混乱)
  • 没法不刷牙就出门(状态不对)

验证平台同理

  • reset:必须先让DUT(被测设计)复位到初始状态,才能发数据
  • configure:必须先配置寄存器,再启动功能
  • main:必须先完成配置,再跑核心业务
  • shutdown:必须先停止数据流,再检查最终结果

💻 三、技术代码示例

示例1:DUT初始化(对应reset系列phase)

class my_test extends uvm_test;// 1. pre_reset:复位前的准备工作(不耗时)taskpre_reset_phase(uvm_phase phase);`uvm_info("PRE_RESET","⚠️ 准备拉低复位信号...",UVM_LOW);// 配置:复位信号初始为高电平dut_vif.reset<=1'b1;endtask// 2. reset:真正的复位操作(消耗仿真时间)taskreset_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("RESET","🔥 复位DUT开始(持续100ns)",UVM_LOW);@(posedge dut_vif.clk);// 等待时钟上升沿dut_vif.reset<=1'b0;// 拉低复位repeat(10)@(posedge dut_vif.clk);// 保持10个周期dut_vif.reset<=1'b1;// 释放复位phase.drop_objection(this);endtask// 3. post_reset:复位后的检查(不耗时)taskpost_reset_phase(uvm_phase phase);`uvm_info("POST_RESET","✅ 检查DUT是否从复位状态恢复",UVM_LOW);if(dut_vif.state!=IDLE)begin `uvm_error("POST_RESET","DUT未回到IDLE状态!");end endtask endclass

示例2:配置寄存器(对应configure系列phase)

class uart_test extends uvm_test;// pre_configure:计算配置参数taskpre_configure_phase(uvm_phase phase);`uvm_info("PRE_CFG","📊 计算波特率分频系数...",UVM_LOW);// 假设系统时钟50MHz,波特率9600baud_divisor=50_000_000/9600;// 结果:5208endtask// configure:通过总线配置DUT(耗时)taskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","🔧 配置UART寄存器...",UVM_LOW);// 写波特率寄存器reg_model.baud_div.write(status,baud_divisor);// 配置8位数据,无校验reg_model.ctrl.write(status,'h03);phase.drop_objection(this);endtask// post_configure:读取回显确认taskpost_configure_phase(uvm_phase phase);uvm_reg_data_tread_val;reg_model.baud_div.read(status,read_val);if(read_val!=baud_divisor)begin `uvm_error("POST_CFG","寄存器配置失败!");end endtask endclass

示例3:主测试(main_phase)

taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","🚀 开始压力测试!",UVM_LOW);// 创建并启动测试序列(就像运动员按指令跑步)uart_tx_seq seq;seq=uart_tx_seq::type_id::create("seq");// 发送1000个随机数据包seq.num_trans=1000;seq.start(env.agent.sequencer);// 等待所有传输完成wait(env.scoreboard.packet_count>=1000);`uvm_info("MAIN","✅ 1000个数据包发送完成",UVM_LOW);phase.drop_objection(this);endtask

场景:测试一个FIFO(先进先出缓冲区)完整示例

class fifo_test extends uvm_test;fifo_env env;// 环境:包含driver, monitor, scoreboard// 1️⃣ build_phase:造人、造设备functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("BUILD","=== 造FIFO测试环境 ===",UVM_LOW);env=fifo_env::type_id::create("env",this);// 配置:FIFO深度=32,数据宽度=32位uvm_config_db#(int)::set(this,"env","fifo_depth",32);endfunction// 2️⃣ connect_phase:连电线functionvoidconnect_phase(uvm_phase phase);super.connect_phase(phase);`uvm_info("CONNECT","=== 连接monitor和scoreboard ===",UVM_LOW);env.agent.monitor.item_port.connect(env.scoreboard.item_export);endfunction// 3️⃣ run_phase的12个子phase(重点!)// 🔄 reset_phase:复位DUTtaskreset_phase(uvm_phase phase);phase.raise_objection(this);// 说:"我要干活!"`uvm_info("RESET","=== 拉低复位信号100ns ===",UVM_LOW);env.driver.reset_dut();// 驱动器拉低reset_n信号#100ns;// 等待100nsphase.drop_objection(this);// 说:"我干完了"endtask// ⚙️ configure_phase:配置FIFOtaskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","=== 配置FIFO工作模式 ===",UVM_LOW);env.driver.write_reg(CONFIG_REG,8'h01);// 使能FIFO#10ns;phase.drop_objection(this);endtask// 🚀 main_phase:压力测试(核心!)taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","=== 开始疯狂写数据 ===",UVM_LOW);// 连续写1000个随机数据for(inti=0;i<1000;i++)begin env.driver.push_data($random());#5ns;end #100ns;// 等待FIFO处理完phase.drop_objection(this);endtask// 📊 report_phase:打印成绩单functionvoidreport_phase(uvm_phase phase);super.report_phase(phase);`uvm_info("REPORT","=== FIFO测试报告 ===",UVM_LOW);`uvm_info("REPORT",$sformatf("写入: %0d个",env.scoreboard.push_count),UVM_LOW);`uvm_info("REPORT",$sformatf("读出: %0d个",env.scoreboard.pop_count),UVM_LOW);`uvm_info("REPORT",$sformatf("错误: %0d个",env.scoreboard.error_count),UVM_LOW);endfunction endclass

🔍 四、时序解剖:精确到ns

时间轴可视化

时间: 0ns 50ns 100ns 150ns 200ns 250ns |-------|-------|-------|-------|-------|→ Phase: [build,connect,start_of_simulation] (瞬间完成) [ reset_phase: 0-50ns ] // 复位信号 [ post_reset, pre_configure ] (各5ns) [ configure_phase: 60-80ns ] // 寄存器配置 [ post_configure, pre_main ] (各5ns) [ main_phase: 90-240ns ] // 核心测试 ❗最长❗ [ post_main, pre_shutdown ] (各5ns) [ shutdown_phase: 250-300ns ] // 收尾 [ extract,check,report,final ] (瞬间完成)

关键观察

  • build/connect0ns就瞬间做完了
  • main_phase占据了**80%**的仿真时间
  • 其他phase都是准备工作或收尾工作

⚠️ 五、常见错误与调试技巧

错误1:在reset_phase发数据

taskreset_phase(uvm_phase phase);phase.raise_objection(this);driver.send_data();// ❌ 错误!DUT还没准备好!#100ns;phase.drop_objection(this);endtask

后果:数据丢失,测试无意义!

正确做法

taskmain_phase(uvm_phase phase);phase.raise_objection(this);driver.send_data();// ✅ 正确!在main_phase发数据phase.drop_objection(this);endtask

错误2:多个组件同时raise objection

// env.driver:taskmain_phase(uvm_phase phase);phase.raise_objection(this);// +1#100ns;phase.drop_objection(this);// -1endtask// env.scoreboard:taskmain_phase(uvm_phase phase);phase.raise_objection(this);// +1 (现在总数=2!)#50ns;phase.drop_objection(this);// -1 (还剩1个)endtask

结果:仿真跑到150ns才结束(等所有objection都drop)

调试技巧:打印objection计数

`uvm_info("OBJ", $sformatf("Objections: %0d", phase.get_objection_count()), UVM_DEBUG);

错误3:为什么要拆这么细?为什么不直接用一个大run_phase搞定所有事?(设计哲学)

答案:为了可重用性可维护性
场景对比

❌ 粗暴方式(所有代码塞在run_phase)
taskrun_phase(uvm_phase phase);phase.raise_objection(this);// 复位(100行代码)dut_vif.reset<=0;#100;dut_vif.reset<=1;// 配置(200行代码)reg_model.baud_div.write(...);reg_model.ctrl.write(...);// 测试(300行代码)seq.start(...);phase.drop_objection(this);endtask

问题:如果10个测试都要复位,代码重复10次!哪天复位信号从低有效改成高有效,要改10个地方!

✅ 优雅方式(按phase拆分)
// 定义一个base_test,专门处理复位和配置class base_test extends uvm_test;taskreset_phase(uvm_phase phase);// 统一复位逻辑(1处维护)endtask taskconfigure_phase(uvm_phase phase);// 统一配置逻辑(1处维护)endtask endclass// 具体测试只关心main_phaseclass test1 extends base_test;taskmain_phase(uvm_phase phase);// 只写test1特有的测试endtask endclass class test2 extends base_test;taskmain_phase(uvm_phase phase);// 只写test2特有的测试endtask endclass

好处:修改复位逻辑,只改base_test,所有子类自动生效!


🎓 六、设计模式:如何优雅地使用Phase

通用测试模板

class your_test extends uvm_test;your_env env;// 构建环境functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);env=your_env::type_id::create("env",this);endfunction// 复位DUT(必需)taskreset_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("RESET","=== 复位DUT ===",UVM_LOW);// TODO: 拉低复位信号#100ns;phase.drop_objection(this);endtask// 配置DUT(可选)taskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","=== 配置DUT ===",UVM_LOW);// TODO: 写寄存器phase.drop_objection(this);endtask// 主测试(核心)taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","=== 开始测试 ===",UVM_LOW);// TODO: 启动sequenceyour_sequence seq;seq=your_sequence::type_id::create("seq");seq.start(env.agent.sequencer);// 等待完成wait(env.scoreboard.all_done());phase.drop_objection(this);endtask// 打印报告functionvoidreport_phase(uvm_phase phase);super.report_phase(phase);`uvm_info("REPORT","=== 测试完成 ===",UVM_LOW);// TODO: 统计结果endfunction endclass

推荐模板(适用于90%的项目):

class my_base_test extends uvm_test;// 1. 【固定套路】在base_test中实现通用逻辑virtual taskreset_phase(uvm_phase phase);// 所有测试都需要的复位endtask virtual taskconfigure_phase(uvm_phase phase);// 所有测试都需要的配置endtask// 2. 【留空给子类】main_phase声明为virtual,但不实现virtual taskmain_phase(uvm_phase phase);`uvm_fatal("MAIN","子类必须重写main_phase!");endtask endclass// 具体测试只需关注main_phaseclass test_tx_only extends my_base_test;taskmain_phase(uvm_phase phase);// 只测试发送功能endtask endclass class test_rx_only extends my_base_test;taskmain_phase(uvm_phase phase);// 只测试接收功能endtask endclass

🎯 七、互动题目(检验掌握程度)

选择题

  1. DUT必须在哪个phase后处于ready状态?

    • A) build_phase
    • B) reset_phase
    • C) main_phase
    • D) report_phase

    答案:B) reset_phase后DUT才复位完成

  2. 核心业务逻辑应该放在哪个phase?

    • A) configure_phase
    • B) main_phase
    • C) shutdown_phase
    • D) final_phase

    答案:B) main_phase是核心测试阶段

  3. 提取仿真结果应该在哪个phase?

    • A) extract_phase
    • B) check_phase
    • C) report_phase
    • D) final_phase

    答案:A) extract_phase专门收集数据


🎓 八、终极记忆口诀

“Build环境,Connect线路,Reset清零,Configure配置,Main干活,Report汇报!”

🎁 记忆卡片(打印贴墙上)

Phase类型作用耗时常见用途
build创建❌ 不耗时new()组件
connect连接❌ 不耗时port.connect()
reset复位✅ 耗时拉低复位信号
configure配置✅ 耗时写寄存器
main测试✅ 耗时发激励、收响应
extract提取❌ 不耗时收集覆盖率
check检查❌ 不耗时assert检查
report报告❌ 不耗时打印日志

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/19 15:30:56

LangFlow结合ChatGPT构建企业级对话系统

LangFlow结合ChatGPT构建企业级对话系统 在客户咨询量激增、服务响应时效要求越来越高的今天&#xff0c;越来越多的企业开始尝试用AI替代或辅助人工客服。但现实往往并不理想&#xff1a;早期的规则引擎机器人“答非所问”&#xff0c;而直接调用大模型又容易“胡说八道”。如…

作者头像 李华
网站建设 2026/2/25 8:23:38

25、负载均衡器深入解析

负载均衡器深入解析 在网络架构中,负载均衡器起着至关重要的作用,它能够合理分配网络流量,提高系统的性能和可用性。下面将详细介绍负载均衡器的相关知识,包括连接跟踪表的查看、超时值设置、数据包处理以及不同的持久连接类型等内容。 查看连接跟踪表 在 2.4 及更高版本…

作者头像 李华
网站建设 2026/2/26 14:46:31

ESG视角下的零工管理:让每一份工作被看见,让每一位劳动者被尊重

“让每一份工作被看见&#xff0c;让每一位劳动者被尊重。”这不仅是盖雅工场的愿景&#xff0c;也是ESG&#xff08;环境、社会和公司治理&#xff09;大背景下&#xff0c;企业零工管理升级的终极目标。在灵工3.0时代&#xff0c;零工管理不再仅仅是关于成本和效率的冰冷计算…

作者头像 李华
网站建设 2026/2/21 13:19:08

AI测试学习记录

一&#xff1a;模型验证留出法&#xff08;适用于大量数据&#xff09;&#xff1a;70%训练数据&#xff0c;15%验证数据&#xff0c;15%测试数据&#xff0c;然后计算平均值和标准差K折交叉验证&#xff08;适用于数据量小&#xff0c;需要稳健评估的场景&#xff09;&#xf…

作者头像 李华
网站建设 2026/2/24 10:10:27

Inventor 二次开发从入门到精通(8)

6.4 尺寸标注与注释的自动化尺寸标注是工程图的关键&#xff0c;API 支持创建尺寸标注、形位公差、文本注释等。6.4.1 创建尺寸标注尺寸标注包括模型尺寸、草图尺寸、自定义尺寸等&#xff0c;可通过Dimensions集合创建&#xff1a;// 创建模型尺寸标注&#xff08;从零件模型关…

作者头像 李华
网站建设 2026/2/22 14:10:14

PaddlePaddle视觉套件PaddleDetection安装包获取与diskinfo下载官网替代方案

PaddlePaddle视觉套件PaddleDetection安装与依赖问题的高效解决方案 在工业质检、智能安防和自动化巡检等实际场景中&#xff0c;开发者常常面临一个看似简单却令人头疼的问题&#xff1a;如何快速、稳定地搭建基于 PaddlePaddle 的计算机视觉开发环境&#xff1f;尽管百度飞桨…

作者头像 李华